Implementing DI & IoC Pattern in MVC 4 with MEF 2 Attributeless using Conventions with RegistrationBuilder in .NET 4.5

  • Update, downloadable sample using MEF with System.ComponentModel.Composition.Web.Mvc Preview 5 – 05/31/2013
  • Update, downloadable sample using Unity 3.0 for MVC 4 – 06/03/2013

From my previous article Generically Implementing the Unit of Work & Repository Pattern with Entity Framework in MVC & Simplifying Entity Graphs – Part 1, we covered how to properly setup your project and implement the Unit Of Work and Repository patterns. In this article, we’ll take our solution the next logical progression, which is getting DI (Dependency Injection) & IoC (Inverse of Control) patterns implemented.

DI & IoC will bring many advantages to our solution. a few of them are as follows.

  1. Programming against abstractions and away from our concrete implementations.
  2. Complementing the previous item, allowing us replace or select concrete implementations during run-time.
  3. Giving us an easy way to mock-up certain implementations for quick unit tests.

Now there are many other debatable benefits for DI & IoC, and I’ll let you make you form your own opinions on this topic, however for the purposes of this post, let’s jump into implementation. We will start off, where we left off in our solution, from our previous post.

First we will need to go ahead and get MEF (Managed Extensibility Framework) wired up, we can get a jump start to this by using our MVC MEF library from my blog post found here.

Note: There are two methods MEF will register exports and imports, one by attributes, which most of know about, the second, is by conventions. I’ve searched high and low on how to get MEF working with MVC using MEF Registrations and have absolutely zero luck, so with that being said, more reason to setup our solution using MEF Registrations (convention based) vs. the Attribute approach

With the Mv4.Mef project added and referenced in our Web project, let’s go ahead and wire up the some code in our Web startup. Now, instead of polluting our Global.asax.cs Application_Start() method with all our MEF Registrations and Conventions, let’s follow the MVC pattern, by adding a MefConfig.cs class under the App_Start folder with the rest of the application start-up code resides.

5-17-2013 7-37-59 PM

Web.App_Start.MefConfig.cs


    public static class MefConfig
    {
        public static void RegisterMef()
        {
            // Set up all the Mef conventions for our web assembly
            var registrationBuilder = new RegistrationBuilder();

            registrationBuilder.ForTypesDerivedFrom<Controller>()
                .SetCreationPolicy(CreationPolicy.NonShared).Export();

            registrationBuilder.ForTypesDerivedFrom<ApiController>()
                .SetCreationPolicy(CreationPolicy.NonShared).Export();

            registrationBuilder
                .ForTypesMatching(t =>
                    t.FullName.StartsWith(
                        Assembly
                            .GetExecutingAssembly()
                            .GetName().Name + ".Parts."))
                .SetCreationPolicy(CreationPolicy.NonShared)
                .ExportInterfaces(x => x.IsPublic);

            var aggregateCatalog = new AggregateCatalog();

            aggregateCatalog.Catalogs.Add(
                new AssemblyCatalog(Assembly.GetExecutingAssembly(), registrationBuilder));

            // Set up all the Mef conventions for our repository assembly
            registrationBuilder = new RegistrationBuilder();

            registrationBuilder.ForTypesDerivedFrom<IUnitOfWork>().Export<IUnitOfWork>();

            aggregateCatalog.Catalogs.Add(
                new AssemblyCatalog(typeof(IUnitOfWork).Assembly, registrationBuilder));

            // Set up all the Mef conventions for our data assembly
            registrationBuilder = new RegistrationBuilder();

            registrationBuilder.ForTypesDerivedFrom<IDbContext>().Export<IDbContext>();

            aggregateCatalog.Catalogs.Add(
                new AssemblyCatalog(typeof(IDbContext).Assembly, registrationBuilder));

            // Add all our catalogs with Mef conventions to our container
            MefMvcConfig.RegisterMef(new CompositionContainer(aggregateCatalog));
        }
    }

Now let’s invoke our MEF configuration from the Global.asax.cs Application_Start() method.

Web.Global.asax.cs


namespace Web
{
    public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            MefConfig.RegisterMef();
            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();
        }
    }
}

So we everything from line 5 to 51 is simply setting up all the import and export mappings from all of our projects or assemblies, I’ll go over some of the conventions we are setting up with the MEF’s RegistrationBuilder.


            registrationBuilder.ForTypesDerivedFrom<Controller>()
                .SetCreationPolicy(CreationPolicy.NonShared).Export();

Her we are saying go ahead and export everything that inherits or is derived from Controller, which means go ahead and add all of our controllers to the Composition Container.


            registrationBuilder.ForTypesDerivedFrom<ApiController>()
                .SetCreationPolicy(CreationPolicy.NonShared).Export();

Here (same principles as the previous item) we are saying go ahead and add all of our Web Api Controllers to the container.


            registrationBuilder
                .ForTypesMatching(t =>
                    t.FullName.StartsWith(
                        Assembly
                            .GetExecutingAssembly()
                            .GetName().Name + ".Parts."))
                .SetCreationPolicy(CreationPolicy.NonShared)
                .ExportInterfaces(x => x.IsPublic);

In this block of code, we are saying go ahead and export everything that implements a public interface as the interface it’s implementing that in the Parts folder in our web project as exampled here http://msdn.microsoft.com/en-us/library/hh708870.aspx.


            registrationBuilder
                .ForTypesDerivedFrom<IUnitOfWork>().Export<IUnitOfWork>();

This is pretty straight forward export anything that is derived from IUnitOfWork as IUnitOfWork. Great, hopefully this sheds some light on how we can setup some conventions for export and import mapping for our Composition Container.

Now let’s revisit to our CustomerController we were working on previously.

Before:


    public class CustomerController : Controller
    {
        public ActionResult Index(int? page)
        {
            var pageNumber = page ?? 1;
            const int pageSize = 20;

            var unitOfWork = new UnitOfWork();

            int totalCustomerCount;

            var customers =
                unitOfWork.Repository<Customer>()
                    .Query()
                    .Include(i => i.CustomerDemographics)
                    .OrderBy(q => q
                        .OrderBy(c => c.ContactName)
                        .ThenBy(c => c.CompanyName))
                    .Filter(q => q.ContactName != null)
                    .GetPage(pageNumber, pageSize, out totalCustomerCount);

            ViewBag.Customers = new StaticPagedList<Customer>(
                customers, pageNumber, pageSize, totalCustomerCount);

            unitOfWork.Save();

            return View();
        }
    }

Now, lets inject all our dependencies that the CustomerController has using one of MEF’s supported Import methods, Constructor Injection.

After:


    public class CustomerController : Controller
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly ICustomerService _customerService;

        public CustomerController(
            IUnitOfWork unitOfWork, 
            ICustomerService customerService)
        {
            _unitOfWork = unitOfWork;
            _customerService = customerService;
        }

        public IUnitOfWork UnitOfWork { get; set; }

        public ActionResult Index(int? page)
        {
            var pageNumber = page ?? 1;
            const int pageSize = 20;

            int totalCustomerCount;

            var customers =
                _unitOfWork.Repository<Customer>()
                    .Query()
                    .Include(i => i.CustomerDemographics)
                    .OrderBy(q => q
                        .OrderBy(c => c.ContactName)
                        .ThenBy(c => c.CompanyName))
                    .Filter(q => q.ContactName != null)
                    .GetPage(pageNumber, pageSize, out totalCustomerCount);

            ViewBag.Customers = new StaticPagedList<Customer>(
                customers, pageNumber, pageSize, totalCustomerCount);

            return View();
        }
    }

Let’s debug and make sure that the IUnitOfWork is getting injected with Constructor Injection from MEF by putting int two breakpoints, first one in the MefMvcControllerFactory so we can take a peek at the Catalog in our Composition Container and the second one in the CustomerController itself.

5-17-2013 5-33-56 PM

Now here we see that all of our previously wired up MEF conventions are valid, we see all of our Controllers, Services, UnitOfWork and NorthwindContext in our CompositionContainer, great! Now for a sanity check, let’s take a look at our CustomerController to ensure that we are actually getting injected now that we validated our container.

5-17-2013 5-43-22 PM

Now let’s just take a quick look at our UnitOfWork and CustomerService objects and notice how there are not attributes decorated anywhere and that they are indeed being added to our CompositionContainer by the conventions we setup earlier with the RegistrationBuilder.

Repostiory.UnitOfWork


namespace Repository
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbContext _context;

        private bool _disposed;
        private Hashtable _repositories;

        public UnitOfWork(IDbContext context)
        {
            _context = context;
            InstanceId = Guid.NewGuid();
        }

        public Guid InstanceId { get; set; }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Save()
        {
            _context.SaveChanges();
        }

        public virtual void Dispose(bool disposing)
        {
            if (!_disposed)
                if (disposing)
                    _context.Dispose();

            _disposed = true;
        }

        public IRepository<T> Repository<T>() where T : class
        {
            if (_repositories == null)
                _repositories = new Hashtable();

            var type = typeof (T).Name;

            if (!_repositories.ContainsKey(type))
            {
                var repositoryType = typeof (Repository<>);

                var repositoryInstance = 
                    Activator.CreateInstance(repositoryType
                            .MakeGenericType(typeof (T)), _context);
                
                _repositories.Add(type, repositoryInstance);
            }

            return (IRepository<T>) _repositories[type];
        }
    }
}

Web.Parts.CustomerService


namespace Web.Parts
{
    public class CustomerService : ICustomerService
    {
        private readonly IUnitOfWork _unitOfWork;

        public CustomerService(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        public void UpdateWithAddressValidation(Customer customer)
        {
                // Example stubbed method, for same UnitOfWork 
                //instance injetction test with page request scoped
        }
    }
}

Awesome, our CustomerController is being handled and instantiated by MEF, therefore it is also handling all the Dependency Injection in the CustomerController e.g. IUnitOfWork and the ICustomerService.

5-17-2013 5-43-22 PM

Now, one very important note, notice that I’ve added a property the UnitOfWork named InstanceId of type Guid. I’ve deliberately drilled down the UnitOfWork.InstanceId in both the UnitOfWork and CustomerService objects in the debug mode screenshot, so that we can see that they are both indeed the same instance. This is very important when using MEF with MVC, that by default, the Scope of the items life cycle are per page request, and will be disposed of after the request has completed. For scenarios where we deliberately want an instance to live for the entire life cycle of the application we can set CreationPolicy for that export to be shared.

Happy Coding..! 🙂

Download sample applications:

Self managed life-cycle using HTTP scoped IUnitOfWork, IDbContext using HttpContext.Current.Items with MEF

https://skydrive.live.com/redir?resid=949A1C97C2A17906!5107&authkey=!AIfx5T8CZ5LlxAs

HTTP scoped using System.ComponentModel.Composition.Web.Mvc Preview 5, which I have upgraded to target .NET 4.5 using MEF

https://skydrive.live.com/redir?resid=949A1C97C2A17906!5108&authkey=!ACJE75BhOdiDSwI

Same solution from post, using the new Unity 3 for MVC 4 instead of MEF
Used the NuGet command install-package Unity.Mvc4 from the Package Manager Console for this one.

https://skydrive.live.com/redir?resid=949A1C97C2A17906!5152&authkey=!APcotdtSZzXZtzo