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

Ray_Liang, 5 Jul 2011 GPL3 156.8K 8.1K 156

   4.65 (58 votes)

 

4.65/5 - 58 votes

3 removed

μ 4.53, σa 1.20 [?]

Add a reason or comment to your vote: x

Adding a comment to your rating is optional

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:

    • Repository Pattern- by Martin fowler
    • Modeling

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

      Hide   Shrink   Copy Code

      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; }

      }

      }

      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:

      Hide   Shrink   Copy Code

      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:

      Hide   Shrink   Copy Code

      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.

      Hide   Shrink   Copy Code

      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.

    Hide   Copy Code

    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.

    Hide   Shrink   Copy Code

    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.

    Hide   Shrink   Copy Code

    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.

    Hide   Copy Code

    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:

    Hide   Shrink   Copy Code

    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:

    Hide   Copy Code

    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));

    }

    That's all, enjoy!

EF DI & MVC的更多相关文章

  1. EF和MVC系列文章导航:EF Code First、DbContext、MVC

    对于之前一直使用webForm服务器控件.手写ado.net操作数据库的同学,突然来了EF和MVC,好多新概念泉涌而出,的确犹如当头一棒不知所措.本系列文章可以帮助新手入门并熟练使用EF和MVC,有了 ...

  2. EF 之 MVC 排序,查询,分页 Sorting, Filtering, and Paging For MVC About EF

    最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精    上篇博客我们学习了EF CodeFirst增删改查 ...

  3. Vs2017 FrameWork EF Mysql Mvc 三层整合1

    1  运行环境   vs2017   Net FromWork 4.6.2  手动版 没有 ado.net 实体数据模型 2 NuGet  MySql.Data.Entity 6.10.9, MySq ...

  4. EF,MVC相关项目请参见→

    End_Test和YM_EF抽象工厂   这两个项目

  5. 关于VS2017 添加 EF的MVC控制器报错的解决方法

    1. 错误描述 :no database provider has been configured fot this DbContext. 此类错误是上下文的注册造成的.解决方式在DBContext中 ...

  6. 采用MiniProfiler监控EF与.NET MVC项目(Entity Framework 延伸系列1)

    前言 Entity Framework 延伸系列目录 今天来说说EF与MVC项目的性能检测和监控 首先,先介绍一下今天我们使用的工具吧. MiniProfiler~ 这个东西的介绍如下: MVC Mi ...

  7. Contoso 大学 - 使用 EF Code First 创建 MVC 应用

    原文 Contoso 大学 - 使用 EF Code First 创建 MVC 应用 Contoso 大学 Web 示例应用演示了如何使用 EF 技术创建 ASP.NET MVC 应用.示例中的 Co ...

  8. Contoso 大学 - 使用 EF Code First 创建 MVC 应用,实例演练

    Contoso 大学 Web 示例应用演示了如何使用 EF 技术创建 ASP.NET MVC 应用.示例中的 Contoso 大学是虚构的.应用包括了类似学生注册.课程创建以及教师分配等功能. 这个系 ...

  9. 第一章 在.net mvc生成EF入门

    一. 打开Visual Studio 2017(我使用的是2017) 新建一个mvc项目 命名为StudentEntity 二.1)建立完项目后在项目中右击选择新建项,找到ADO.NET实体数据模型 ...

随机推荐

  1. golang开发环境配置及Beego框架安装

    配置环境:Windows7推荐IDE:LiteIDEGO下载地址:http://www.golangtc.com/downloadBeego开发文档地址:http://beego.me/docs/in ...

  2. phpstorm 63342默认端口怎么修改

    phpstorm进行网页调试的时候,默认是加端口号63342,在配置本地php环境的时候默认端口不一定是63342这个,更多的是系统默认的端口号80,那么问题就出现了,如何在phpstorm中将633 ...

  3. 1Z0-053 争议题目解析707

    1Z0-053 争议题目解析707 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 707.Because of a logical corruption in the EMPLOY ...

  4. SSH整合时执行hibernate查询报错:java.lang.ClassCastException: com.ch.hibernate.Department_$$_javassist_0 cannot be cast to javassist.util.proxy

    今天在整合ssh三个框架时,有一个功能,是查询所有员工信息,且员工表和部门表是多对一的映射关系,代码能正常运行到查询得到一个List集合,但在页面展示的时候,就报异常了, java.lang.Clas ...

  5. 【JUC】JDK1.8源码分析之AbstractQueuedSynchronizer(二)

    一.前言 在锁框架中,AbstractQueuedSynchronizer抽象类可以毫不夸张的说,占据着核心地位,它提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.所以很有必 ...

  6. android防止内存溢出浅析

    Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M.但是Android采用的是Java语言编写,所以在很大程度上,Android的内存机制等同于Java的内存机制,在刚开始开 ...

  7. JS中的this

    JS中的this 最近在学习JavaScript面向对象,其中有个难点就是关于this的问题. 关于this,我们要知道的两个问题,一个是this指向什么?另一个是this可以写在哪? 关于this的 ...

  8. MySQL PXC构建一个新节点只需IST传输的方法

    需求场景:原有的pxc环境数据量已经比较大,新买的服务器要加入此集群中,如何让其用IST的方式传输,而不是SST. PXC传输数据有两种方式: IST: Incremental State Trans ...

  9. iOS 视图与视图层次结构(内容根据iOS编程)

    视图基础 视图是 UIView 对象,或者其子对象. 视图知道如何绘制自己. 视图可以处理事件,例如触摸(touch). 视图会按照层次结构排列,位于视图层次结构顶端的是应用窗口. 视图层次结构 任何 ...

  10. 写出好的 commit message

    为何要关注提交信息 加快Reviewing Code的过程 帮助我们写好release note 5年后帮你快速想起来某个分支,tag或者 commit增加了什么功能,改变了哪些代码 让其他的开发者在 ...