Thursday 6 October 2011

StructureMap + MVC3 + Generic Repository Pattern

I had this exact same problem:

Have a Generic Repository:

public interface IRepository<TEntity> : IDisposable where TEntity : class
     { }
and a Concrete Implementation:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
     { }
which i wanted injected into the constructor of the Controllers at runtime where the TEntity would be the Model relevant to that Controller:
    public FooBarController(IRepository<FOO_BAR_TYPE> repository)
            {
                _repo = repository;
            }
the Controller would then use Repository "_repo" to update the Model:
    //
    // POST: /EmergencyServiceType/Create
    [HttpPost]
    public ActionResult Create(FOO_BAR_TYPE foobar)
    {
        if (ModelState.IsValid)
        {            
            // GetNextSequenceValue() deals with Oracle+EF issue with auto-increment IDs
            foobar.FOO_BAR_ID = _repo.GetNextSequenceValue(); 
            _repo.Add(foobar);
            _repo.SaveChanges();
            return RedirectToAction("Index");  
        }
    
        return View(foobar); // display the updated Model
    }
simonjreid elluded to the answer for me: had to add the ObjectContext to the StructureMap configuration (the Repository's purpose was to wrap up the Context generated by EntityFramework, which i called MyContextWrapper. Therefore because the Repository depended on MyContextWrapper, which in turn depends on ObjectContext):
    // This avoids 'No Default Instance for ...DbConnection' exception
    x.For<System.Data.Objects.ObjectContext>().Use<MyContextWrapper>();
    x.For<System.Web.Mvc.IController>().Use<Controllers.FooBarController>().Named("foobarcontroller"); // note that Named is required and is Case Sensitive
However, now i get the StructureMap runtime Exception:

StructureMap Exception Code: 205
Missing requested Instance property "connectionString"

After reading a post by Jeremy Miller [A Gentle Quickstart][1] (right at the bottom) i found that you can define what arguments to pass into the constructor of your registered types ie i needed to pass in the Connection String to the Constructor of the MyCustomContext class (here is the full listing of how i am initializing the ObjectFactory:
    string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["MyContextWrapper"].ConnectionString;
    ObjectFactory.Initialize(x =>
                {
                    x.Scan(scan =>
                            {
                                // Make sure BUSINESS_DOMAIN assembly is scanned
                                scan.AssemblyContainingType<BUSINESS_DOMAIN.MyContextWrapper>(); 
                                scan.TheCallingAssembly();
                                scan.WithDefaultConventions();
                            });
                    // 'connStr' below is a local variable defined above
                    x.For<System.Data.Objects.ObjectContext>()
                        .Use<MyContextWrapper>()
                        .Ctor<string>().Is(connStr);
                    x.For<System.Web.Mvc.IController>().Use<Controllers.FooBarController>().Named("foobarcontroller"); 
                });
And BOOM! Can now have my Controller instaniated at runtime by StructureMap and get it to inject an instance of IRepository...happy days.

[1]: http://structuremap.net/structuremap/QuickStart.htm

No comments:

Post a Comment