软件开发就像是一个江湖,而设计模式就是一本高深的秘籍每读一次、用一次、想一次都能得到新的领悟,让我们的设计技能有所提高。开始时我们可能会“为了模式而模式”,让代码变得乱78糟甚至难以让人理解,但随着设计能力的提高与模式的运用和理解,慢慢地我们可能会忘掉模式随心所欲,此时再读读代码或者你已经发现自己的工程已融合模式之美—"模式为设计而生,设计为需求而活"。在开篇突然想分享一下这10几年用模式的一点小小的领悟。

IRepository 与 IUnitOfWork

在2011年我曾在Codeproject 发表过题为 "The Repository Pattern with EF Code First & Dependency Injection in ASP.NET MVC3" 的文章,当时讲述的是Repository模式和Ioc如何结合EF在ASP.NET MVC3上应用。历时3年多对Repository的使用,最近又有新的感悟。 Repository 是这几年我用得最多的模式之一,而且也是我认为最常用和最容易掌握的一种模式,我使用Repository的目的有二:

  • 将数据的实例化解耦,通过Ioc或是工厂方法构造数据对象而不是用 "new"
  • 将数据的逻辑行为(CURD)解耦,这样会便于我更换任何形式的数据库,无论底层数据库使用的是MSSQL还是MongoDB我都可以采用相同的访问逻辑。

以下是我期望在代码中看到的效果

public class PostController:Contrller
{private IRepository repository;public MyController(IRepository repository){this.repository=repository;}public Action Get(int id) {return View(repository.Find(id));}[HttpPost]public Action Create(Post post) {repository.Add(post);     repository.Submit();return this.Get(post.ID);} ...
}

这里使用了构造注入,由MVC向Controller注入Repository的实例,这样我就不需要在使Repository的时候去创建它了。(这种例子你Google会发现很多,包括在asp.net上也不少)

单一地使用Repository很容易理解,但运用在项目中的实例情况可以这样吗?大多数答案是否定的,如果当前的Controller控制的不单纯是一个实体,而是多个实体时,如果使用纯Repository的话代码会变糟:

public class PostController:Contrller
{private IRepository posts;private IRepository blogs;private IRepository owners;public MyController(IRepository blogs,IRepository posts,IRepository owners){this.posts=posts;this.blogs=blogs;this.owners=owners;}public Action Get(int id) {var post=posts.Find(id)var blog=blogs.Find(post.BlogID);var owner=posts.Find(post.Owner);...return View(new { Post=post, Blog=blog, Owner=owner });}...
}

一看上去似乎没什么大问题,只是在构造时多了两个Repository的注入,但是我们的目只有一个Controller吗? 而每个实体都需要去实现一个Repository? 这样的写法在项目中散播会怎么样呢?

最后你会得到一大堆鸡肋式的"Repository",他们的相似度非常大。或是你使用一个Repository实现处理所有的实体,但你需要在构造时进行配置 (注:构造注入并不代表不构造,而是将构造代码放在了一个统一的地方),更糟的情况是,如果使用的是继承于IRepository的子接口,那么Controller就会与IRepository的耦合度加大。

很明显 Repository 不是一种独立的模式,它自身和其它模式有很强的相关度,如果只是拿它来独立使用局限性会很大。我们需要其它的接口去统一“管理”Repository和避免在Controller内显式的出现Repository的类型声明。将上面的代码改一下:

public class PostController:Contrller
{private IUnitOfWorks works;public PostController(IUnitOfWorks works){this.works=works;}public Action Get(int id) {var post=works.Posts.Find(id)var blog=works.Blogs.Find(post.BlogID);var owner=works.Posts.Find(post.Owner);...return View(new { Post=post, Blog=blog, Owner=owner });}[HttpPost]public Action Create(Post post){works.Add(post);...}
}

这里就引入了 UnitOfWorks 模式,将对所有的Repository的耦合消除(把所有的Repository变量的声明去掉了),

IRepository与IUnitOfWorks两个模式是最佳组合,我们可以通过对UnitOfWorks一个类进行IoC处理,而调用方代码则集中从IUnitOfWorks对象获取所需的Repository,接下来看看他们的定义吧

IRepository 接口

    /// <summary>/// 定义通用的Repository接口/// </summary>/// <typeparam name="T"></typeparam>public interface IRepository<T>: IDisposablewhere T : class{/// <summary>/// 获取所有的实体对象/// </summary>/// <returns></returns>IQueryable<T> All();/// <summary>/// 通过Lamda表达式过滤符合条件的实体对象/// </summary>IQueryable<T> Filter(Expression<Func<T, bool>> predicate);
/// <summary>/// Gets the object(s) is exists in database by specified filter./// </summary>bool Contains(Expression<Func<T, bool>> predicate);/// <summary>/// 获取实体总数/// </summary>int Count();int Count(Expression<Func<T, bool>> predicate);/// <summary>/// 通过键值查找并返回单个实体/// </summary>T Find(params object[] keys);/// <summary>/// 通过表达式查找复合条件的单个实体/// </summary>/// <param name="predicate"></param>T Find(Expression<Func<T, bool>> predicate);/// <summary>/// 创建实体对象/// </summary>
        T Create(T t);/// <summary>/// 删除实体对象/// </summary>void Delete(T t);/// <summary>/// 删除符合条件的多个实体对象/// </summary>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>
        T Update(T t);/// <summary>/// Clear all data items./// </summary>/// <returns>Total clear item count</returns>void Clear();/// <summary>/// Save all changes./// </summary>/// <returns></returns>int Submit();}

IUnitOfWorks 接口

    public interface IUnitOfWorks{IQueryable<T> Where<T>(Expression<Func<T, bool>> predicate) where T : class;IQueryable<T> All<T>() where T : class;int Count<T>() where T : class;int Count<T>(Expression<Func<T, bool>> predicate) where T : class;        T Find<T>(object id) where T : class;T Find<T>(Expression<Func<T, bool>> predicate) where T : class;        T Add<T>(T t) where T : class;        IEnumerable<T> Add<T>(IEnumerable<T> items) where T : class;        void Update<T>(T t) where T : class;        void Delete<T>(T t) where T : class;void Delete<T>(Expression<Func<T, bool>> predicate) where T : class;        void Clear<T>() where T : class;

        int SaveChanges();void Config(IConfiguration settings);}

仔细一看你会发现IRepository和IUnitOfWorks的定义非常相似,在使用的角度是UnitOfWorks就是所有实例化的Repository的一个统一“包装”, 这是我在发表 "The Repository Pattern with EF Code First & Dependency Injection in ASP.NET MVC3" 后对IUnitOfWorks在实用上的一种扩展。以前的方式是直接在UnitOfWorks的属性的中暴露一个Repository实例给外部使用,但这样做的话会降低IUnitOfWorks的通用性。所以我让IUnitOfWorks使用起来更像是一个IRepository.看一段代码的比较:

//之前的做法,Posts是一个IRepository的实现,是不是很像EF
var post=works.Posts.Add(new Post()); //C
works.Posts.Update(post);//U
var post=works.Posts.Get(id); //R
works.Posts.Delete(post);//D//优化后的IUnitOfWorks
var post=works.Add(new Post());//C
works.Update(post);//U
var post=works.Get(id); //R
works.Delete(post);//D

如果将IRepository通过属性的方式暴露给调用方,IUnitOfWorks的扩展性就会下降,而且会令IUnitOfWorks的实现类与调用方建立很紧密的耦合。我对IUnitOfWorks优化后以泛型决定使用哪一个Repository,这样可以将IUnitOfWorks与调用方进行解耦。所有的实体通过一个通用Repository实现,这样可以避免为每一个实体写一个Repository。而对于具有特殊处理逻辑的Repository才通过属性暴露给调用方。

IRepository 与 IUnitOfWorks的实现

在这里我会先实现一套使用EF访问数据库的通用 Repository 和 UnitOfWorks

EntityRepository 

    public class EntityRepository<TContext, TObject> : IRepository<TObject>where TContext : DbContextwhere TObject : class{protected TContext context;protected DbSet<TObject> dbSet;protected bool IsOwnContext = false;/// <summary>/// Gets the data context object./// </summary>protected virtual TContext Context { get { return context; } }/// <summary>/// Gets the current DbSet object./// </summary>protected virtual DbSet<TObject> DbSet { get { return dbSet; } }/// <summary>/// Dispose the class./// </summary>public void Dispose(){if ((IsOwnContext) && (Context != null))Context.Dispose();GC.SuppressFinalize(this);}/// <summary>/// Get all objects./// </summary>/// <returns></returns>public virtual IQueryable<TObject> All(){return  DbSet.AsQueryable();}/// <summary>/// Gets objects by specified predicate./// </summary>/// <param name="predicate">The predicate object.</param>/// <returns>return an object collection result.</returns>public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> predicate){return  DbSet.Where(predicate).AsQueryable<TObject>();}public bool Contains(Expression<Func<TObject, bool>> predicate){return DbSet.Count(predicate) > 0;}/// <summary>/// Find object by keys./// </summary>/// <param name="keys"></param>/// <returns></returns>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 (IsOwnContext)Context.SaveChanges();return newEntry;}public virtual void Delete(TObject TObject){var entry = Context.Entry(TObject);DbSet.Remove(TObject);if (IsOwnContext)Context.SaveChanges();}public virtual TObject Update(TObject TObject){var entry = Context.Entry(TObject);DbSet.Attach(TObject);entry.State = EntityState.Modified;if (IsOwnContext)Context.SaveChanges();return TObject;}public virtual int Delete(Expression<Func<TObject, bool>> predicate){var objects = DbSet.Where(predicate).ToList();foreach (var obj in objects)DbSet.Remove(obj);if (IsOwnContext)return Context.SaveChanges();return objects.Count();}public virtual int Count(){return DbSet.Count();}public virtual int Count(Expression<Func<TObject, bool>> predicate){return DbSet.Count(predicate);}public int Submit(){return Context.SaveChanges();}     public virtual void Clear(){}}

UnitOfWorks

public class UnitOfWorks<TDBContext> : IUnitOfWorkswhere TDBContext :DbContext{protected TDBContext dbContext;public UnitOfWorks<TDBContext>(TDBContext context){dbContext=context;}//构造通用的Repositoryprivate IDictionary<Type,object> repositoryTable = new Dictionary<Type,object>();        //注册其它的Repositorypublic void Register<T>(IRepository<T> repository){var key=typeof(T);if (repositoryTable.ContainsKey(key))repositoryTable[key].Add(repository);}private IRepository<T> GetRepository<T>()where T:class{IRepository<T> repository = null;var key=typeof(T);if (repositoryTable.ContainsKey(key))repository = (IRepository<T>)repositoryTable[key];else{repository = GenericRepository<T>();repositoryTable.Add(key, repository);}return repository;}protected virtual IRepository<T> GenericRepository<T>() where T : class{return new EntityRepository<T>(dbContext);}public T Find<T>(object id) where T : class{return GetRepository<T>().Find(id);}public T Add<T>(T t) where T : class{return GetRepository<T>().Create(t);}public IEnumerable<T> Add<T>(IEnumerable<T> items) where T : class{var list = new List<T>();foreach (var item in items)list.Add(Add(item));return list;}public void Update<T>(T t) where T : class{GetRepository<T>().Update(t);}public void Delete<T>(T t) where T : class{GetRepository<T>().Delete(t);}public void Delete<T>(Expression<Func<T, bool>> predicate) where T : class{GetRepository<T>().Delete(predicate);}public int SaveChanges(bool validateOnSave = true){if (!validateOnSave)dbContext.Configuration.ValidateOnSaveEnabled = false;return dbContext.SaveChanges();}public void Dispose(){if (dbContext != null)dbContext.Dispose();GC.SuppressFinalize(this);}public System.Linq.IQueryable<T> Where<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate)where T:class{return GetRepository<T>().Filter(predicate);}public T Find<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T : class{return GetRepository<T>().Find(predicate);}public System.Linq.IQueryable<T> All<T>() where T : class{return GetRepository<T>().All();}public int Count<T>() where T : class{return GetRepository<T>().Count();}public int Count<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T : class{return GetRepository<T>().Count(predicate);}public void Config(IConfiguration settings){ var configuration=settings as DbConfiguration ;if (configuration != null){this.dbContext.Configuration.AutoDetectChangesEnabled = configuration.AutoDetectChangesEnabled;this.dbContext.Configuration.LazyLoadingEnabled = configuration.LazyLoadingEnabled;this.dbContext.Configuration.ProxyCreationEnabled = configuration.ProxyCreationEnabled;this.dbContext.Configuration.ValidateOnSaveEnabled = configuration.ValidateOnSaveEnabled;}}public void Clear<T>() where T : class{GetRepository<T>().Clear();}int IUnitOfWorks.SaveChanges(){return this.SaveChanges();}
}

接下来看看如何使用这套基于EF的实现,首先是对Model的定义

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

调用方:使用UnitOfWorks和Repository

var works=new UnitOfWorks(new DB());var pc=works.Add(new Category()
{  Name="PC",Title="电脑"
});workds.Add(new Product(){Category=pc,Name="iMac",Title="iMac"Price=9980
})works.SaveChanges();

注:如果需要使用IoC方式构造UnitOfWorks 可参考我在 "The Repository Pattern with EF Code First & Dependency Injection in ASP.NET MVC3" 一文中提及如何在MVC内通过Unity 实现DI.

小结

以上述例子为例,如果我们想将Category存储于文本文件而不想改动调用方的代码。我们可以实现一个 FileBaseCategoryRepository,然后在UnitOfWorks在构造后调用Register方法将默认的Category Repository替换掉,同理,这就可以建立不同的Repository去配置UnitOfWork

var works=new UnitOfWorks(new DB());
works.Register(new FileBaseCategoryRepository());var pc=works.Add(new Category()
{  Name="PC",Title="电脑"
});workds.Add(new Product(){Category=pc,Name="iMac",Title="iMac"Price=9980
})works.SaveChanges();

使用IUnitOfWorks+IRepository模式你就可以灵活地配置你的数据访问方式,可以通过Repository极大地提通实体存储逻辑代码的重用性。

相关参考

  • Repository Pattern - by Martin fowler
  • The Repository Pattern with EF Code First & Dependency Injection in ASP.NET MVC3 - by Ray

转载于:https://www.cnblogs.com/Ray-liang/p/3809822.html

Repository模式与UnitOfWorks模式的运用相关推荐

  1. Repository(资源库)模式

    Repository(资源库) 协调领域和数据映射层,利用类似于集合的接口来访问领域对象 定义(来自Martin Fowler的<企业应用架构模式>): Mediates between ...

  2. java仓储模式_仓储(Repository)和工作单元模式(UnitOfWork)

    仓储和工作单元模式 仓储模式 为什么要用仓储模式 通常不建议在业务逻辑层直接访问数据库.因为这样可能会导致如下结果: 重复的代码 编程错误的可能性更高 业务数据的弱类型 更难集中处理数据,比如缓存 无 ...

  3. Hibernate锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作

    介绍 在我以前的文章中 ,我解释了OPTIMISTIC锁定模式是如何工作的,以及它如何帮助我们同步外部实体状态更改. 在本文中,我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的 ...

  4. 休眠锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作

    介绍 在我以前的文章中 ,我解释了OPTIMISTIC锁定模式是如何工作的,以及它如何帮助我们同步外部实体状态更改. 在本文中,我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的 ...

  5. MVC模式和DDD模式对比,谁才是银弹?

    目录 MVC模式和DDD模式对比,谁才是银弹? 从DDD的角度看MVC架构的问题 第一层:初出茅庐 第二层:草船借箭(战术设计) 第三层:运筹帷幄(战略设计) DDD的不足 总结 MVC模式和DDD模 ...

  6. (七)Spring Security (spring-cloud-starter-oauth2)应用详解------认证授权服务------授权码模式和密码模式

    OAuth2.0介绍 OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方应用或分享他们数据的所有内容.OAuth2. ...

  7. RabbitMQ 入门系列(11)— RabbitMQ 常用的工作模式(simple模式、work模式、publish/subscribe模式、routing模式、topic模式)

    1. simple 模式 simple 模式是最简单最常用的模式 2. work 模式 work 模式有多个消费者 消息产生者将消息放入队列.生产者系统不需知道哪一个任务执行系统在空闲,直接将任务扔到 ...

  8. GOF23设计模式(创建型模式)工厂模式

    目录: 一:工厂模式的核心本质 二:关于面向对象的六大基本原则 三:工厂模式的三大类详解(代码示例,详细分析) 首先,上咱本GOF23所有工厂模式的分类表格!!! 创建型模式 单例模式.工厂模式.抽象 ...

  9. C中文件操作的文本模式和二进制模式,到底有啥区别?

    在C中,使用fopen打开文件有两种模式:一种是文本模式,一种是二进制模式.那这两种模式之间有什么区别,是不是使用文本模式打开的文件就只能使用文本函数比如fprintf来操作,而使用二进制打开的文件就 ...

最新文章

  1. php 导航高亮,zblogphp导航高亮代码分享教程
  2. C语言对mysql数据库的操作
  3. 三分钟破解无线网——无线网络安全攻防
  4. linux 开机启动 自启动 设置
  5. Maximum Product of Word Lengths
  6. C++ Primer 5th笔记(chap 14 重载运算和类型转换)函数匹配与重载运算符
  7. [蓝桥杯][算法提高VIP]最小乘积(提高型)-排序
  8. 将十进制数转为N进制的方法
  9. 朱兴杰(1986-),男,泰康保险集团股份有限公司数据信息中心应用创新高级工程师...
  10. 个人学习进度条------第二周
  11. python3默认编码标准是_python3 编码
  12. Navicat 连接Oracle时提示oracle library is not loaded的问题解决
  13. 在Web页面中控制其元素的选择状态
  14. mdui前端插件,有菜单对话框等
  15. 《安富莱嵌入式周报》第246期:2021.12.27--2022.01.02
  16. 做食材配送行业,哪个平台软件比较好?
  17. caffe的安装配置(CUDA7.5+VS2013)
  18. 10办公软件软件分享
  19. 基于WEB信息管理系统测试时应考虑的因素有哪些
  20. 让动画不再僵硬:Facebook Rebound Android动画库介绍

热门文章

  1. JSP中javaBean和Servlet理解
  2. python debug【】
  3. 贝叶斯统计:信噪对偶与Dawid定理
  4. Linux新建用户图解
  5. Linux TCPIP内核参数优化
  6. GIS空间数据结构 - 矢量和栅格
  7. asp.net ashx 学习总结
  8. 云时代架构阅读笔记十三——你的系统如何处理高并发?
  9. DevExpress控件之GridControl、GridView
  10. QT中显示gif图片方法