Bounded DbContext with Generic Unit of Work, Generic Repositories, Entity Framework 6 & EntLib Unity 3.0 in MVC 4

Update: 08/12/2013 – Changed InjectionConstructor parameter to: ResolvedParameter<IDbContext>(), to trigger compilation of the container when setting up the DbBounded Context and UnitOfWork(s) registrations.

Update: 08/08/2013 – Added PerRequestLifetimeManager() to the IUnitOfWork Unity Registration (binding) in UnityConfig.cs, so that the life-cycle of the UnitOfWork(s) instances being injected have singleton behavior within the scope of an Http request.

Update: 08/07/2013 – Ken from Microsoft has been kind enough to reach out and inform those of us that are using EF4 or EF5, that there maybe some potential collision issues, if there are entities with overlapping names, even if they live in different assemblies, please read below for a potential solution for this. If this does not apply to your use case or scenario, please continue on to the blog post after the block-quote.

At the risk of spamming your blog in comments I am turning to email. This is Ken the poster on your blog. J Your BoundedContext implementation has another interesting usage to easily support multiple DbContexts. Something that isn’t always that easy to do with a Repo + UoW frameworks. However, with EF5 and probably EF4 your readers will run into a bug if they have entities with overlapping names – EVEN IF they are separated by namespaces or live in different assemblies. For instance say you have two databases that both have a Logging table.

ExceptionMessage: The mapping of CLR type to EDM type is ambiguous because multiple CLR types match the EDM type ‘MyType’. Previously found CLR type ‘Namespace1.MyTable’, newly found CLR type ‘Namespace2.MyTable’. The mapping of CLR type to EDM type is ambiguous because multiple CLR types match the EDM type ‘ReferenceTable’. Previously found CLR type ‘Namespace1.ReferenceTable’, newly found CLR type ‘Namespace2.ReferenceTable’.”

The issue occurs with at EF5 unsure about EF4 but I suspect so. Read more here: http://entityframework.codeplex.com/workitem/911

The issue is resolved in EF6 beta1 from my testing.

Codewise this would be setup as follows

 

UnityConfig.cs
 
container.RegisterType("DbContext1");
container.RegisterType("DbContext2");
container.RegisterType(
    "DbContext1UnitOfWork", new InjectionConstructor(container.Resolve("DbContext1")));
container.RegisterType(
    "DbContext2UnitOfWork", new InjectionConstructor(container.Resolve("DbContext2")));
 
An Api Controller
 
public class SomethingFromDbContext1Controller : ApiController
    {
        private readonly IUnitOfWork _uow;
 
        public GenericRaptorTicketController(
            [Dependency("DbContext1UnitOfWork ")] IUnitOfWork uow)
        {
            _uow = uow;
        }

Now all of the above logic in the controller goes to Database1 using the types specified by namespace (dealing with overlapping table names that resulted in POCO classes that had the same name, different namespace). Easily I could add a second, third, fourth controller and specify DbContext2UnitOfWork and point to a second database. Cool stuff. Your approach is creative and I am sharing it with my peers and customers.

Now if only I have find a T4 template to bend to my will to shape the Data Mappings and Entities. Simon Huge’s Reverse POCO template comes close with a few modifications. J

-Ken

So there was an interesting question that was raised over the weekend from Tim, on could we take our generic Unit of Work and Repositories and implement the Bounded DbContext Pattern or philosophy if you will from DDD (Domain Driven Design) concepts. There are a few reasons to go with this Pattern e.g organization, manageability, decoupling, performance (in some cases), maintainability, etc.

My favorite reason is when working with large databases and having functionality in your application that is only working with specific domain areas, why load up a DbContext that has the overhead of your entire entity graph when your only working with a specific few? For example, you may have a database that has close to 100 tables (e.g. AdventureWorks), however if a user is only managing Products on a screen, why load up a DbContext that has the overhead of the entire entity graph. Figuring out where to decouple and decompose your domain model, to implement the Bounded DbContext Pattern can span a wide array of reasons, those reasons could span from business to technical reasons, usually both.

As an example, the AdventureWorks database is already separated into domain SQL Schemas, each of the tables shown here are prefixed with the SQL Schema. This is somewhat of an example of which entities would be in a Bounded DbContext, a Bounded DbContext could be created for each of the SQL Schema’s, and each of the Bounded DbContext’s would have the tables as DbSet’s in them. Again, separating your domain into areas really depends on your use cases both business and technical, this is just an example of a starting point.

7-31-2013 2-52-45 PM

Example: Potential Bounded DbContext’s in AdventureWorks based on SQL schemas defined.

  • HumanDbcontext
  • PersonDbcontext
  • ProductionDbcontext
  • PurchasingDbcontext
  • SalesDbcontext

Anyhow, back to the topic at hand, with some minor changes, here’s how we can accomplish Bounded DbContext with our UnitOfWork and Generic Repositories, we’ll start off from our last post: Generically Implementing the Unit of Work & Repository Pattern with Entity Framework in MVC & Simplifying Entity Graphs. We are using the Northwind database as an example since this was used in the previous post, however with a database schema of this size, it’s probably not the ideal candidate for Bounded DbContext, you would probably implement this pattern on a database that had a much larger schema. But for the objective of this blog, Northwind will do. :)

Note: although we are using EF6 (alpha) in this example, we aren’t using any of EF6′s new features, however, it was a bit of a wiggle to get everything working. If you are attempting to get MVC, EF6 & SQL Sever CE 4.0 working, than this post and download maybe of use.

Data.NorthwindContext.cs – Before


    public class NorthwindContext : DbContext, IDbContext
    {
        static NorthwindContext()
        {
            Database.SetInitializer<NorthwindContext>(null);
        }

        public NorthwindContext()
            : base("Name=NorthwindContext")
        {
            Configuration.LazyLoadingEnabled = false;
        }

        public new IDbSet<T> Set<T>() where T : class
        {
            return base.Set<T>();
        }

        public override int SaveChanges()
        {
            this.ApplyStateChanges();
            return base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new CategoryMap());
            modelBuilder.Configurations.Add(new CustomerDemographicMap());
            modelBuilder.Configurations.Add(new CustomerMap());
            modelBuilder.Configurations.Add(new EmployeeMap());
            modelBuilder.Configurations.Add(new Order_DetailMap());
            modelBuilder.Configurations.Add(new OrderMap());
            modelBuilder.Configurations.Add(new ProductMap());
            modelBuilder.Configurations.Add(new RegionMap());
            modelBuilder.Configurations.Add(new ShipperMap());
            modelBuilder.Configurations.Add(new SupplierMap());
            modelBuilder.Configurations.Add(new TerritoryMap());
            modelBuilder.Configurations.Add(new InvoiceMap());
        }
    }

After

Data.DbContextBase.cs

We’ll go ahead abstract out our DbContext into a base class, since we’ll have multiple Bounded DbContexts.


    public abstract DbContextBase : DbContext, IDbContext
    {
        public DbContextBase(string nameOrConnectionString) : 
            base(nameOrConnectionString)
        {
            Configuration.LazyLoadingEnabled = false;
        }

        public new IDbSet<T> Set<T>() where T : class
        {
            return base.Set<T>();
        }

        public override int SaveChanges()
        {
            this.ApplyStateChanges();
            return base.SaveChanges();
        }
    }

Data.NorthwindCustomerDataContext.cs
*Customer Bounded Context


    public class NorthwindCustomerContext : DbContextBase
    {
        static NorthwindCustomerContext()
        {
            Database.SetInitializer<NorthwindCustomerContext>(null);
        }

        public NorthwindCustomerContext()
            : base("Name=NorthwindContext")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new CustomerDemographicMap());
            modelBuilder.Configurations.Add(new CustomerMap());
        }
    }

Data.NorthwindDataContext – Everything else, Bounded Context :p


    public class NorthwindContext : DbContextBase
    {
        static NorthwindContext()
        {
            Database.SetInitializer<NorthwindCustomerContext>(null);
        }

        public NorthwindContext()
            : base("Name=NorthwindContext")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new CategoryMap());
            modelBuilder.Configurations.Add(new EmployeeMap());
            modelBuilder.Configurations.Add(new Order_DetailMap());
            modelBuilder.Configurations.Add(new OrderMap());
            modelBuilder.Configurations.Add(new ProductMap());
            modelBuilder.Configurations.Add(new RegionMap());
            modelBuilder.Configurations.Add(new ShipperMap());
            modelBuilder.Configurations.Add(new SupplierMap());
            modelBuilder.Configurations.Add(new TerritoryMap());
            modelBuilder.Configurations.Add(new InvoiceMap());
        }
    }

We’ll need the following EntLib Unity v3.0 NuGet Packages.

  • Unity v3.0
  • Unity bootstrapper for ASP.NET MVC v3.0
  • Unity bootstrapper for ASP.NET MVC Web API v3.0

7-30-2013 9-29-07 AM

Spa.App_Start.UnityConfig.csUnity Bindings Before


container.RegisterType<IDbContext, NorthwindContext>();
container.RegisterType<IUnitOfWork, UnitOfWork>();
 

Spa.App_Start.UnityConfig.cs – Unity Bindings After (with Registration Names)


        public static void RegisterTypes(IUnityContainer container)
        {
            container.RegisterType<IDbContext, NorthwindContext>(new PerRequestLifetimeManager(), "NorthwindContext");
            container.RegisterType<IDbContext, NorthwindCustomerContext>(new PerRequestLifetimeManager(), "NorthwindCustomerContext");
            
            container.RegisterType<IUnitOfWork, UnitOfWork>(
                "NorthwindUnitOfWork", new InjectionConstructor(new ResolvedParameter<IDbContext>("NorthwindContext")));
            
            container.RegisterType<IUnitOfWork, UnitOfWork>(
                "NorthwindCustomerUnitOfWork", new InjectionConstructor(new ResolvedParameter<IDbContext>("NorthwindCustomerContext")));
        }
 

When working with ASP.NET (web apps) remember to make sure you are making good use of the UnityPerRequestHttpModule (line 12, below) in your UnityWebActivator. This will default the lifetime of your instances to lifetime of the current HttpRequest. You can configure registrations and pass in a other specific lifetime manager’s for other registration configurations who’s life-cycle does not need to bound to the HttpRequest.

Spa.App_Start.UnityWebActivator.cs


    public static class UnityWebActivator
    {
        public static void Start() 
        {
            var container = UnityConfig.GetConfiguredContainer();

            FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
            FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));

            DependencyResolver.SetResolver(new UnityDependencyResolver(container));

             DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
        }
    }
  

Now we could just instantiate and pass in the appropriate Bounded DbContext implementations into the UnitOfWork registrations, however we would defeat one of the fundamental reasons of DI & IoC to begin with e.g. when we write our unit test later, we aren’t going to be able to switch out DbContext with a mocked one, easily. We could even do this registration in the web.config to give us more flexibility in terms of swapping the implementations of our DbContext’s however for the purposes of this post, we’ll continue on pro-grammatically.

Spa.Controllers.CustomerController – Before

Well now, that we have Bounded DbContext and UnitOfworks, how do we get them? We have two options, first options which is leveraging DI & IoC with Unity 3.0, and the obvious method of instantiating them manually. We’ll demonstrate the first option below, in our CustomerController.


    public class CustomerController : Controller
    {
        private readonly IUnitOfWork _unitOfWork;

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

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

            int totalCustomerCount;

            var customers =
                _unitOfWork.Repository<Customer>()
                    .Query()
                    .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();
        }

        [HttpGet]
        public ActionResult Edit(string id)
        {
            var customer = _unitOfWork.Repository<Customer>().FindById(id);
            return View(customer);
        }

        [HttpPost]
        public ActionResult Edit(Customer customer)
        {
            if (ModelState.IsValid)
                RedirectToAction("Edit");

            customer.State = ObjectState.Modified;
            _unitOfWork.Repository<Customer>().Update(customer);
            _unitOfWork.Save();

            return View(customer);
        }
    }

Spa.CustomerController – After

We can get them by passing the registration name of Unity binding we setup earlier.

Option A:


    public class CustomerController : Controller
    {
        private readonly IUnitOfWork _customerUnitOfWork;
        private readonly IUnitOfWork _northwindUnitOfWork;

        public CustomerController(IUnityContainer container)
        {
            _northwindUnitOfWork = container.Resolve<IUnitOfWork>("NorthwindUnitOfWork");;
            _customerUnitOfWork = container.Resolve<IUnitOfWork>("NorthwindCustomerUnitOfWork");
        }

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

            int totalCustomerCount;

            var customers =
                _customerUnitOfWork.Repository<Customer>()
                    .Query()
                    .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();
        }

        [HttpGet]
        public ActionResult Edit(string id)
        {
            var customer = _customerUnitOfWork.Repository<Customer>().FindById(id);
            return View(customer);
        }

        [HttpPost]
        public ActionResult Edit(Customer customer)
        {
            if (ModelState.IsValid)
                RedirectToAction("Edit");

            customer.State = ObjectState.Modified;
            _customerUnitOfWork.Repository<Customer>().Update(customer);
            _customerUnitOfWork.Save();

            return View(customer);
        }
    }

Option B:


    public class CustomerController : Controller
    {
        private readonly IUnitOfWork _customerUnitOfWork;
        private readonly IUnitOfWork _northwindUnitOfWork;

        public CustomerController(
            [Dependency("NorthwindUnitOfWork")] IUnitOfWork northwindUnitOfWork,
            [Dependency("NorthwindCustomerUnitOfWork")] IUnitOfWork customerUnitOfWork)
        {
            _northwindUnitOfWork = northwindUnitOfWork;
            _customerUnitOfWork = customerUnitOfWork;
        }

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

            int totalCustomerCount;

            var customers =
                _customerUnitOfWork.Repository<Customer>()
                    .Query()
                    .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();
        }

        [HttpGet]
        public ActionResult Edit(string id)
        {
            var customer = _customerUnitOfWork.Repository<Customer>().FindById(id);
            return View(customer);
        }

        [HttpPost]
        public ActionResult Edit(Customer customer)
        {
            if (ModelState.IsValid)
                RedirectToAction("Edit");

            customer.State = ObjectState.Modified;
            _customerUnitOfWork.Repository<Customer>().Update(customer);
            _customerUnitOfWork.Save();

            return View(customer);
        }
    }

Note: Probably a good idea, specially in this case to go ahead and create an Enum or a class with constants instead of passing in hand coded strings as the registration name.

I prefer Option B, personally I don’t like the fact that you injecting anything with the entire Container, I rather have it when something is requesting to be injected, that the requester is specific in what it requesting for. Anyhow, I’ve seen this debate go both ways, moving on…

The alternative for those of us that are not using any IoC & DI

You should be using some form of DI & IoC with the N-number of frameworks out there, however if your not, obviously you an instantiate your Bounded UnitOfwork and DbContext directly.

Spa.CustomerController – without IoC and/or DI


    public class CustomerController : Controller
    {
        private readonly IUnitOfWork _customerUnitOfWork;
        private readonly IUnitOfWork _northwindUnitOfWork;

        public CustomerController(IUnityContainer container)
        {
            _northwindUnitOfWork = new UnitOfWork(new NorthwindContext());
            _customerUnitOfWork = new UnitOfWork(new NorthwindCustomerContext());
        }

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

            int totalCustomerCount;

            var customers =
                _customerUnitOfWork.Repository<Customer>()
                    .Query()
                    .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();
        }

        [HttpGet]
        public ActionResult Edit(string id)
        {
            var customer = _customerUnitOfWork.Repository<Customer>().FindById(id);
            return View(customer);
        }

        [HttpPost]
        public ActionResult Edit(Customer customer)
        {
            if (ModelState.IsValid)
                RedirectToAction("Edit");

            customer.State = ObjectState.Modified;
            _customerUnitOfWork.Repository<Customer>().Update(customer);
            _customerUnitOfWork.Save();

            return View(customer);
        }
    }
 

Now, let’s run the application.

http://localhost:29622/Customer

7-30-2013 12-35-41 AM

There you have it, Happy Coding…! :)

Download sample application: https://skydrive.live.com/redir?resid=949A1C97C2A17906!5962

Note: Please “Enable NuGet Package Restore” on the VS Solution.