Click here to Skip to main content
Click here to Skip to main content

The Repository Pattern with EF Code First & Dependency Injection in ASP.NET MVC3

, 5 Jul 2011 GPL3
Rate this:
Please Sign up or sign in to vote.
In this article, I will explain how to implement Respository pattern with EF4.1 code first and how to enable dependency injection feature to your ASP.NET MVC3 Applications.

Introduction

These days, I started using EF code first and thought about how could I take advantage from it for my recent project. My project is based on MVC3 and there are some challenges to continue updating this project. At that time, I must finish my job in a very short deadline so fast so good I just define some data access interfaces and implemented them with EF4.

There are some things in my project troubling me:

  • I have used the EF entities in my controllers anywhere that there is a connection translation error throwing infrequently.
  • Modules coupling degree becoming closed.
  • Manage services and contexts life cycle are difficult.

Obviously, it needs to refactor my project with a new architecture designing.

Preparing

Before starting my work, I need to do some researching and learning. I have read a large numbers of articles about DDD, SOA, EF code first, Dependency Injection pattern, Repository pattern even watch all web casts about MVC on ASP.NET and a week later...

I have really found some useful resources about those topics and I am sharing them below:

Modeling

At first, I need to create the data model classes (the POCO objects) for this demo I will create Category and Product.

namespace Demo.DAL
{
    public class Category
    {
        [Key]
        public int ID { get; set; }

        public virtual string Name { get; set; }

        public virtual string Title { get; set; }

        public virtual ICollection<Product> Products { get; set; }
    }

    public class Product
    {
        [Key]
        public int ID { get; set; }
        
        public int CategoryID { get; set; }

        [ForeignKey("CategoryID")]
        public virtual Category Category {get;set;}

        public string Name { get; set; }

        public string Title { get; set; }

        public string Description{get;set;}

        public decimal Price { get; set; }
    }

    public class DB : DbContext
    {
        public DB() : base("DemoDB") { }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Product> Products { get; set; }
    }
} 

model-diagram.png

Repository Pattern with EF Code First

When I finish my learning on EF code first and Repository, I couldn't find any solutions for implementing Repository pattern with EF code first. EF4.1 is so powerful for building the DAL that it allows us to use POCO objects to define our database and BO (business objects) instead of inheriting from Entity that could give me a big hand. I could very easily define the repository interface like below:

public interface IRepository<T>: IDisposable  where T : class
{
        /// <summary>
        /// Gets all objects from database
        /// </summary>
        IQueryable<T> All();

       /// <summary>
        /// Gets objects from database by filter.
        /// </summary>
        /// <param name="predicate">Specified a filter</param>
        IQueryable<T> Filter(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// Gets objects from database with filting and paging.
        /// </summary>
        /// <typeparam name="Key"></typeparam>
        /// <param name="filter">Specified a filter</param>
        /// <param name="total">Returns the total records count of the filter.</param>
        /// <param name="index">Specified the page index.</param>
        /// <param name="size">Specified the page size</param>
        IQueryable<T> Filter<Key>(Expression<Func<T, bool>> filter , 
			out int total, int index = 0, int size = 50);

        /// <summary>
        /// Gets the object(s) is exists in database by specified filter.
        /// </summary>
        /// <param name="predicate">Specified the filter expression</param>
        bool Contains(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// Find object by keys.
        /// </summary>
        /// <param name="keys">Specified the search keys.</param>
        T Find(params object[] keys);

        /// <summary>
        /// Find object by specified expression.
        /// </summary>
        /// <param name="predicate"></param>
        T Find(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// Create a new object to database.
        /// </summary>
        /// <param name="t">Specified a new object to create.</param>
        T Create(T t);

        /// <summary>
        /// Delete the object from database.
        /// </summary>
        /// <param name="t">Specified a existing object to delete.</param>        
        void Delete(T t);

        /// <summary>
        /// Delete objects from database by specified filter expression.
        /// </summary>
        /// <param name="predicate"></param>
        int Delete(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// Update object changes and save to database.
        /// </summary>
        /// <param name="t">Specified the object to save.</param>
        int Update(T t);

        /// <summary>
        /// Get the total objects count.
        /// </summary>
        int Count { get; }
}

Now create a general repository for EF code first that implement the IRepository:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Entity;
using System.Collections.Generic;
using System.Data;

namespace Demo.DAL
{
    public class Repository<TObject> : IRepository<TObject>

        where TObject : class
    {
        protected DB Context;
        protected DB Context = null;
        private bool shareContext = false;

        public Repository()
        {
            Context = new DB();
        }

        public Repository(DB context)
        {
            Context = context;
            shareContext = true;
        }

        protected DbSet<TObject> DbSet
        {
            get
            {
                return Context.Set<TObject>();
            }
        }

        public void Dispose()
        {
            if (shareContext && (Context != null))
                Context.Dispose();
        }

        public virtual IQueryable<TObject> All()
        {
            return DbSet.AsQueryable();
        }

        public virtual IQueryable<TObject> 
		Filter(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.Where(predicate).AsQueryable<TObject>();
        }

        public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> filter,
		 out int total, int index = 0, int size = 50)
        {
            int skipCount = index * size;
            var _resetSet = filter != null ? DbSet.Where(filter).AsQueryable() : 
				DbSet.AsQueryable();
            _resetSet = skipCount == 0 ? _resetSet.Take(size) : 
				_resetSet.Skip(skipCount).Take(size);
            total = _resetSet.Count();
            return _resetSet.AsQueryable();
        }

        public bool Contains(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.Count(predicate) > 0;
        }

        public virtual TObject Find(params object[] keys)
        {
            return DbSet.Find(keys);
        }

        public virtual TObject Find(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.FirstOrDefault(predicate);
        }

        public virtual TObject Create(TObject TObject)
        {
            var newEntry = DbSet.Add(TObject);
            if (!shareContext)
                Context.SaveChanges();
            return newEntry;
        }

        public virtual int Count
        {
            get
            {
                return DbSet.Count();
            }
        }

        public virtual int Delete(TObject TObject)
        {
            DbSet.Remove(TObject);
            if (!shareContext)
                return Context.SaveChanges();
            return 0;
        }

        public virtual int Update(TObject TObject)
        {
            var entry = Context.Entry(TObject);
            DbSet.Attach(TObject);
            entry.State = EntityState.Modified;
            if (!shareContext)
                return Context.SaveChanges();
            return 0;
        }

        public virtual int Delete(Expression<Func<TObject, bool>> predicate)
        {
            var objects = Filter(predicate);
            foreach (var obj in objects)
                DbSet.Remove(obj);
            if (!shareContext)
                return Context.SaveChanges();
            return 0;
        }
    }
} 

The Repository has two running modes: exclusive mode and shared mode.

  • Exclusive mode: The data context is generated by Repository, the data objects only use in the Repository's data context (Update, Delete).
  • Shared mode: In many scenarios, we maybe use over 1 repository at the same time. If repositories have their own data context, it may cause the data duplicate issue. So we need to pass the shared data context to repositories in transaction on construction.

To make this example more close to a reality project, I have defined two repository interfaces for Category and Product.

public interface ICategoryRepository:IRepository<Category>
{
    string GetUrl();
}

public interface IProductRepository : IRepository<Product> 
{
    string ResolvePicture();
}
    
public class CategoryRepository : Repository<Category>, ICategoryRepository
{
    public CategoryRepository(DB context) : base(context) { }

    public string GetUrl()
    {
        return "";
    }
}
    
public class ProductRepostiroy : Repository<Product>, IProductRepository
{
    public ProductRepostiroy(DB context) : base(context) { }

    public string ResolvePicture()
    {
        return "";
    }
}

In order to share data context, I use the UnitOfWork design pattern to maintain the data context and interoperability repository life time.

UnitOfWork Pattern

According to Martin Fowler, the Unit of Work pattern "maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems."

We could implement the UnitOfWork pattern in two ways:

  1. Implement a GeneralRepository<T> class and embed in UnitOfWork. Then define the wapper methods and expose them to invoke the GeneralRepository<T>. But I don't think it's the best way, because the UnitOfWork will become very common and I couldn't use any subclass of Repository.
  2. Inject the IRepository descendant interfaces into UnitOfWork.
public interface IUnitOfWork:IDisposable
{
    int SaveChanges();
}

public interface IDALContext : IUnitOfWork
{
    ICategoryRepository Categories { get; }
    IProductRepository Products { get; }
}

IUnitOfWork interface is very simple, it is only used to save changes, the IDALContext is use to define the IRepository descendant interfaces, construct and maintain the data context and Repository classes.

public class DALContext : IDALContext
{
    private DB dbContext;
    private ICategoryRepository categories;
    private IProductRepository products;

    public DALContext()
    {
        dbContext = new DB();
    }

    public ICategoryRepository Categories
    {
        get
        {
            if (categories == null)
                categories = new CategoryRepository(dbContext);
            return categories;
        }
    }

    public IProductRepository Products
    {
        get 
        {
            if (products == null)
                products = new ProductRepostiroy(dbContext);
            return products;
        }
    }

    public int SaveChanges()
    {
        throw new NotImplementedException();
    }

    public void Dispose()
    {
        if (categories != null)
            categories.Dispose();
        if (products != null)
            products.Dispose();
        if (dbContext != null)
            dbContext.Dispose();
        GC.SuppressFinalize(this);
    }
}

Service Layer

The DAL has been done! Constantly we need to create the SL (Service Layer). Now I create a new service interface to get categories, products or create new product.

Through the service layer, used to encapsulate data level of implementation details, call to the interface should be designed to be simple as possible. Service layer is usually called by the Controller in the MVC, Controller only needs to use the service interface, and the DI is responsible for construction, such a Controller and data layer of coupling is greatly reduced.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Practices.Unity;
using Demo.DAL;

namespace Demo.Services
{
    public interface ICatalogService
    {
        List<Category> GetCategories();
        List<Product> GetProducts();
        Product CreateProduct(string categoryName, string productName, int price);
    }

    public class CatalogService : ICatalogService, IDisposable
    {
        private IDALContext context;

        public CatalogService(IDALContext dal)
        {
            context = dal;
        }

        public List<Category> GetCategories()
        {
            return context.Categories.All().ToList();
        }

        public List<Product> GetProducts()
        {
            return context.Products.All().ToList();
        }

        public Product CreateProduct(string categoryName, string productName, int price)
        {
            var category = new Category() { Name = categoryName };
            var product = new Product() 
			{ Name=productName,Price=price,Category=category };
            context.Products.Create(product);
            context.SaveChanges();
            return product;
        }

        public void Dispose()
        {
            if (context != null)
                context.Dispose();
        }
    }
}  

Controller

Pass the ICatalogService instance to controller by used construction injection.

public class HomeController : Controller
{
    private ICatalogService service;

    public HomeController(ICatalogService srv)
    {
        service = srv;
    }

    public ActionResult Index()
    {
        ViewData.Model = service.GetCategories();
        return View();
    }
}

OK, that is what I want. Finally, we need to construct and "Inject" instances to this object model.

Dependency Injection in MVC3

In MVC3, we could use the DependencyResolver.SetResolver(IDependencyResolver resolver) to register a Dependency Injection Container and use IDependencyResolver.GetService() method to locate our registered service instance that is a very amazing feature in this version. For more about DI in MVC3, you could read the "ASP.NET MVC 3 Service Location" below.

I have read that many developers like to create a new ControllerFactory to inject the controller instance. Actually that is not necessary! Because the MVC3 will call DependencyResolver.GetService to construe the Controller, so I only need to do one thing: Implement the IDependencyResolver.

Unity

We could found many popular DI framework in Google such as Castle Windsor, Structuremap,ObjectBuilder, Managed Extensibility Framework (MEF) and Microsoft Unity, I'd like to use Microsoft Unity 2.0 because the MVC DI features in comes from it that means we could very easy to implement DI in our MVC applications.

References from MSDN:

Unity is a lightweight, extensible dependency injection container that supports interception, constructor injection, property injection, and method call injection. You can use Unity in a variety of different ways to help decouple the components of your applications, to maximize coherence in components, and to simplify design, implementation, testing, and administration of these applications.

Unity is a general-purpose container for use in any type of Microsoft® .NET Framework-based application. It provides all of the features commonly found in dependency injection mechanisms, including methods to register type mappings and object instances, resolve objects, manage object lifetimes, and inject dependent objects into the parameters of constructors and methods and as the value of properties of objects it resolves.
In addition,

Unity is extensible. You can write container extensions that change the behavior of the container, or add new capabilities. For example, the interception feature provided by Unity, which you can use to add policies to objects, is implemented as a container extension.

Implement IDependencyResolver

The next step is create a DependencyResolver for MVC:

namespace Demo.Web
{
    public class UnityDependencyResolver : IDependencyResolver
    {
        readonly IUnityContainer _container;

        public UnityDependencyResolver(IUnityContainer container)
        {
            this._container = container;
        }

        public object GetService(Type serviceType)
        {
            try
            {
                return _container.Resolve(serviceType);
            }
            catch
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            try
            {
                return _container.ResolveAll(serviceType);
            }
            catch
            {
                return new List<object>();
            }
        }
    }
} 

Open the Global.asax and register types and set DependencyResolver in Application_Start:

protected void Application_Start() {
            AreaRegistration.RegisterAllAreas();
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            var container = new UnityContainer();
            container.RegisterType<ICatalogService, CatalogService>
				(new PerThreadLifetimeManager())
                          .RegisterType<IDALContext, DALContext>();
            DependencyResolver.SetResolver(new UnityDependencyResolver(container));
} 
run-result.png

That's all, enjoy!

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

Ray_Liang
Architect DotNetAge
China China
In 1999, I started programming using Delphi, VB, VJ.From 2002 I started with .NET using C#.Since 2005 when i had became an EIP product manager I was focus on EIP and CMS technique. In 2008 i established dotnetage.com and started to shared my ideas and projects online. I believe "No shared no grow"
 
www.dotnetage.com
Follow on   Twitter

Comments and Discussions

 
AnswerRe: Injection of Unit of Work PinmemberRay_Liang30-Aug-13 11:28 
GeneralMy vote of 5 PinmemberChristoph Keller1-Mar-13 2:41 
GeneralNice Article...! PinmemberSampath Kumar Sathiya20-Dec-12 20:55 
QuestionNeed help in implementing Custom Membership using Repository Pattern PinmemberMember 321208025-Oct-12 4:11 
QuestionEF Doesnt workink with bulk ,so if I loop tro Entitys and Save ,memory will Choking......(see:Nhibernet) Pinmemberdudu82762-Sep-12 2:12 
QuestionUnit of work question Pinmemberrussell.mccloy10-Aug-12 15:39 
GeneralMy vote of 5 Pinmemberrussell.mccloy10-Aug-12 14:48 
Questionwhy northwind.sdf there? Pinmemberzahidinfo8-Aug-12 9:03 
GeneralMy vote of 4 PinmemberFlorian Rappl5-Apr-12 3:30 
GeneralMy vote of 5 Pinmembercarloscalvog29-Mar-12 10:49 
Questionsave changes and unit of work Pinmembereasten14-Mar-12 22:55 
QuestionNice article, a question about disposing... Pinmemberemusti28-Feb-12 4:03 
BugRe: Nice article, a question about disposing... Pinmembercode71130-Aug-13 10:16 
GeneralMy vote of 5 PinmemberRakesh_Kalange9-Feb-12 23:18 
GeneralMy vote of 1 PinmemberNavinBiz8-Dec-11 10:53 
GeneralRe: My vote of 1 Pinmemberjspaey11-Mar-12 7:42 
QuestionGood article! PinmemberSebastian Pederiva28-Aug-11 0:17 
QuestionUpdate function doesn't work PinmemberJfarr11-Aug-11 9:09 
AnswerRe: Update function doesn't work Pinmemberphilmee956-Sep-11 8:04 
QuestionHow would you unit test this and fake the IDALContext Pinmemberlangman6622-Jul-11 6:39 
AnswerRe: How would you unit test this and fake the IDALContext PinmemberRay_Liang24-Jul-11 18:09 
Questionwhats function of shareContext in Repository class Pinmembercaothangchv6-Jul-11 13:31 
AnswerRe: whats function of shareContext in Repository class PinmemberRay_Liang6-Jul-11 17:31 
GeneralVery nice PinmemberPYtony10-Jun-11 6:02 
GeneralRe: Very nice PinmemberRay_Liang6-Jul-11 17:36 
The demo 's bug is fixed, you would try it again now.
GeneralMy vote of 3 PinmemberNicolas Dorier9-Jun-11 11:33 
General[My vote of 2] Down side of a generic repository, 2 for me. PinmemberNicolas Dorier9-Jun-11 11:20 
GeneralRe: [My vote of 2] Down side of a generic repository, 2 for me. Pinmemberjspaey11-Mar-12 7:58 
GeneralRe: [My vote of 2] Down side of a generic repository, 2 for me. PinmemberNicolas Dorier11-Mar-12 10:13 
GeneralRe: [My vote of 2] Down side of a generic repository, 2 for me. Pinmemberjspaey11-Mar-12 22:37 
GeneralRe: [My vote of 2] Down side of a generic repository, 2 for me. Pinmemberjspaey11-Mar-12 8:26 
GeneralMy vote of 4 PinmemberZeroDotNet8-Jun-11 10:29 
GeneralMy vote of 3 Pinmemberbomt7-Jun-11 21:37 
GeneralInheritance? Pinmemberhazard9837-Jun-11 13:33 
GeneralRe: Inheritance? PinmemberRay_Liang6-Jul-11 17:42 
GeneralRe: Inheritance? Pinmemberhazard98318-Jul-11 3:49 
GeneralGood post PinmemberShahriar Iqbal Chowdhury7-Jun-11 11:30 
GeneralMy vote of 5 PinmemberMonjurul Habib7-Jun-11 10:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411019.1 | Last Updated 5 Jul 2011
Article Copyright 2011 by Ray_Liang
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid