DDD分层架构之仓储(层超类型基础篇)

前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能。

  仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合。仓储中有很多操作都是可以通用的,可以把这部分操作抽取到基类中。

  在Util.Domains项目中创建一个文件夹Repositories,这个文件夹用来放仓储相关的接口。在Repositories下创建一个仓储接口IRepository

  把仓储基接口放到Util.Domains,是因为仓储接口是在领域层定义的,这与传统三层架构的数据访问层接口的位置不同。

  仓储是基础设施层的组成部分,位于领域层的下方,按理来说,领域层应该依赖仓储,但这会导致领域层与具体数据访问组件耦合,降低了领域层的复用能力。为了让领域层更加纯净,可以应用依赖倒置原则(DIP。依赖倒置原则提到,高层模块不应该依赖低层模块,这里的高层模块就是领域层,低层模块是基础设施层的仓储。

  依赖倒置原则反转了两者的依赖关系,即仓储反过来依赖于领域层。为了让领域层可以访问到仓储提供的服务,需要抽取仓储接口,你可以把这些接口放到独立的程序集中,但这会增加不必要的开销。一种更好的方法是直接把低层接口放入使用它们的客户程序集中,这样可以简化设计,仅在必要时才分离出独立的接口程序集。依赖倒置原则不仅适用于仓储,对于任何可能导致高耦合的基础设施服务都适用,比如第三方外部接口,发邮件,发短信等。

  在Util.Datas.Ef项目中创建一个仓储实现类Repository

  仓储接口IRepository的代码如下。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using Util.Datas;
  6.  
  7. namespace Util.Domains.Repositories {
  8. /// <summary>
  9. /// 仓储
  10. /// </summary>
  11. /// <typeparam name="TEntity">实体类型</typeparam>
  12. /// <typeparam name="TKey">实体标识类型</typeparam>
  13. public interface IRepository<TEntity, in TKey> where TEntity : class, IAggregateRoot<TKey> {
  14. /// <summary>
  15. /// 添加实体
  16. /// </summary>
  17. /// <param name="entity">实体</param>
  18. void Add( TEntity entity );
  19. /// <summary>
  20. /// 添加实体
  21. /// </summary>
  22. /// <param name="entities">实体</param>
  23. void Add( IEnumerable<TEntity> entities );
  24. /// <summary>
  25. /// 修改实体
  26. /// </summary>
  27. /// <param name="entity">实体</param>
  28. void Update( TEntity entity );
  29. /// <summary>
  30. /// 移除实体
  31. /// </summary>
  32. /// <param name="id">实体标识</param>
  33. void Remove( TKey id );
  34. /// <summary>
  35. /// 移除实体
  36. /// </summary>
  37. /// <param name="entity">实体</param>
  38. void Remove( TEntity entity );
  39. /// <summary>
  40. /// 查找实体集合
  41. /// </summary>
  42. List<TEntity> FindAll();
  43. /// <summary>
  44. /// 查找实体集合
  45. /// </summary>
  46. IQueryable<TEntity> Find();
  47. /// <summary>
  48. /// 查找实体
  49. /// </summary>
  50. /// <param name="id">实体标识</param>
  51. TEntity Find( params object[] id );
  52. /// <summary>
  53. /// 查找实体列表
  54. /// </summary>
  55. /// <param name="ids">实体标识列表</param>
  56. List<TEntity> Find( IEnumerable<TKey> ids );
  57. /// <summary>
  58. /// 判断实体是否存在
  59. /// </summary>
  60. /// <param name="predicate">条件</param>
  61. bool Exists( Expression<Func<TEntity, bool>> predicate );
  62. /// <summary>
  63. /// 索引器查找,获取指定标识的实体
  64. /// </summary>
  65. /// <param name="id">实体标识</param>
  66. TEntity this[TKey id] { get; }
  67. /// <summary>
  68. /// 保存
  69. /// </summary>
  70. void Save();
  71. /// <summary>
  72. /// 获取工作单元
  73. /// </summary>
  74. IUnitOfWork GetUnitOfWork();
  75. }
  76. }
  1. using System;
  2.  
  3. namespace Util.Domains.Repositories {
  4. /// <summary>
  5. /// 仓储
  6. /// </summary>
  7. /// <typeparam name="TEntity">实体类型</typeparam>
  8. public interface IRepository<TEntity> : IRepository<TEntity, Guid> where TEntity : class, IAggregateRoot<Guid> {
  9. }
  10. }

  仓储实现类Repository的代码如下。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data.Entity;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using Util.Domains;
  7. using Util.Domains.Repositories;
  8.  
  9. namespace Util.Datas.Ef {
  10. /// <summary>
  11. /// 仓储
  12. /// </summary>
  13. /// <typeparam name="TEntity">实体类型</typeparam>
  14. /// <typeparam name="TKey">实体标识类型</typeparam>
  15. public abstract class Repository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : class, IAggregateRoot<TKey> {
  16. /// <summary>
  17. /// 初始化仓储
  18. /// </summary>
  19. /// <param name="unitOfWork">工作单元</param>
  20. protected Repository( IUnitOfWork unitOfWork ) {
  21. UnitOfWork = (EfUnitOfWork)unitOfWork;
  22. }
  23.  
  24. /// <summary>
  25. /// Ef工作单元
  26. /// </summary>
  27. protected EfUnitOfWork UnitOfWork { get; private set; }
  28.  
  29. /// <summary>
  30. /// 添加实体
  31. /// </summary>
  32. /// <param name="entity">实体</param>
  33. public void Add( TEntity entity ) {
  34. UnitOfWork.Set<TEntity>().Add( entity );
  35. UnitOfWork.CommitByStart();
  36. }
  37.  
  38. /// <summary>
  39. /// 添加实体
  40. /// </summary>
  41. /// <param name="entities">实体</param>
  42. public void Add( IEnumerable<TEntity> entities ) {
  43. if ( entities == null )
  44. return;
  45. UnitOfWork.Set<TEntity>().AddRange( entities );
  46. UnitOfWork.CommitByStart();
  47. }
  48.  
  49. /// <summary>
  50. /// 修改实体
  51. /// </summary>
  52. /// <param name="entity">实体</param>
  53. public virtual void Update( TEntity entity ) {
  54. UnitOfWork.Entry( entity ).State = EntityState.Modified;
  55. UnitOfWork.CommitByStart();
  56. }
  57.  
  58. /// <summary>
  59. /// 移除实体
  60. /// </summary>
  61. /// <param name="id">实体标识</param>
  62. public void Remove( TKey id ) {
  63. var entity = Find( id );
  64. if ( entity == null )
  65. return;
  66. Remove( entity );
  67. }
  68.  
  69. /// <summary>
  70. /// 移除实体
  71. /// </summary>
  72. /// <param name="entity">实体</param>
  73. public void Remove( TEntity entity ) {
  74. UnitOfWork.Set<TEntity>().Remove( entity );
  75. UnitOfWork.CommitByStart();
  76. }
  77.  
  78. /// <summary>
  79. /// 查找实体集合
  80. /// </summary>
  81. public List<TEntity> FindAll() {
  82. return Find().ToList();
  83. }
  84.  
  85. /// <summary>
  86. /// 查找实体
  87. /// </summary>
  88. public IQueryable<TEntity> Find() {
  89. return UnitOfWork.Set<TEntity>();
  90. }
  91.  
  92. /// <summary>
  93. /// 查找实体
  94. /// </summary>
  95. /// <param name="id">实体标识</param>
  96. public TEntity Find( params object[] id ) {
  97. return UnitOfWork.Set<TEntity>().Find( id );
  98. }
  99.  
  100. /// <summary>
  101. /// 查找实体列表
  102. /// </summary>
  103. /// <param name="ids">实体标识列表</param>
  104. public List<TEntity> Find( IEnumerable<TKey> ids ) {
  105. if ( ids == null )
  106. return null;
  107. return Find().Where( t => ids.Contains( t.Id ) ).ToList();
  108. }
  109.  
  110. /// <summary>
  111. /// 索引器查找,获取指定标识的实体
  112. /// </summary>
  113. /// <param name="id">实体标识</param>
  114. public TEntity this[TKey id] {
  115. get { return Find( id ); }
  116. }
  117.  
  118. /// <summary>
  119. /// 判断实体是否存在
  120. /// </summary>
  121. /// <param name="predicate">条件</param>
  122. public bool Exists( Expression<Func<TEntity, bool>> predicate ) {
  123. return Find().Any( predicate );
  124. }
  125.  
  126. /// <summary>
  127. /// 保存
  128. /// </summary>
  129. public void Save() {
  130. UnitOfWork.Commit();
  131. }
  132.  
  133. /// <summary>
  134. /// 获取工作单元
  135. /// </summary>
  136. public IUnitOfWork GetUnitOfWork() {
  137. return UnitOfWork;
  138. }
  139. }
  140. }
  1. using System;
  2. using Util.Domains;
  3.  
  4. namespace Util.Datas.Ef {
  5. /// <summary>
  6. /// 仓储
  7. /// </summary>
  8. /// <typeparam name="TEntity">实体类型</typeparam>
  9. public abstract class Repository<TEntity> : Repository<TEntity, Guid> where TEntity : class, IAggregateRoot<Guid> {
  10. /// <summary>
  11. /// 初始化仓储
  12. /// </summary>
  13. /// <param name="unitOfWork">工作单元</param>
  14. protected Repository( IUnitOfWork unitOfWork )
  15. : base( unitOfWork ) {
  16. }
  17. }
  18. }

  仓储是对聚合的操作,所以泛型接口声明IRepository<TEntity, in TKey> where TEntity : class, IAggregateRoot<TKey>对TEntity类型限定为聚合。

  在Repository实现类中,通过注入DbContext工作单元来完成所有的工作。IUnitOfWorkEfUnitOfWork是在应用程序框架实战十九:工作单元层超类型中定义的,EfUnitOfWork从DbContext派生,它有一个核心方法CommitByStart,用来告诉仓储,如果开启了工作单元就等待调用端通过Commit提交,否则立即提交。每个数据更新方法Add、Update、Remove都会调用CommitByStart方法。

  对于使用Entity Framework 进行Update修改操作有多种实现方式。在仓储基类中实现的Update方法比较通用,但我手工编写代码时一般会直接把聚合取出来,修改聚合属性,再提交工作单元。

  本文介绍了仓储通用操作的基本实现,后续文章我将介绍如何通过表达式树对查询扩展和封装。

  .Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。

  谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/

  下载地址:http://files.cnblogs.com/xiadao521/Util.2014.12.17.1.rar

版权所有,转载请注明出处 何镇汐的技术博客
 

DDD分层架构之仓储的更多相关文章

  1. 应用程序框架实战二十二 : DDD分层架构之仓储(层超类型基础篇)

    前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操作都是可以通用的,可以把这部分操作抽取到 ...

  2. 应用程序框架实战二十一:DDD分层架构之仓储(介绍篇)

    前面已经介绍过Entity Framework的工作单元和映射层超类型的封装,从本文开始,将逐步介绍仓储以及对查询的扩展支持. 什么是仓储 仓储表示聚合的集合. 仓储所表现出来的集合外观,仅仅是一种模 ...

  3. 应用程序框架实战十八:DDD分层架构之聚合

    前面已经介绍了DDD分层架构的实体和值对象,本文将介绍聚合以及与其高度相关的并发主题. 我在之前已经说过,初学者第一步需要将业务逻辑尽量放到实体或值对象中,给实体“充血”,这样可以让业务逻辑高度内聚, ...

  4. 应用程序框架实战十三:DDD分层架构之我见

    前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...

  5. 应用程序框架实战十三:DDD分层架构之我见(转)

    前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...

  6. DDD分层架构之聚合

    DDD分层架构之聚合 前面已经介绍了DDD分层架构的实体和值对象,本文将介绍聚合以及与其高度相关的并发主题. 我在之前已经说过,初学者第一步需要将业务逻辑尽量放到实体或值对象中,给实体“充血”,这样可 ...

  7. DDD分层架构之我见

    DDD分层架构之我见 前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定 ...

  8. 4、传统三层架构与DDD分层架构

    4.传统三层架构与DDD分层架构 模型是抽象的 现实是形象的 技巧是重要的 思想是永恒的 从传统三层架构与DDD分层架构的编程演变其实是思想的演变. 传统三层架构,即用户界面层UI.业务逻辑层BAL. ...

  9. 应用程序框架实战十七:DDD分层架构之值对象(层超类型篇)

    上一篇介绍了值对象的基本概念,得到了一些朋友的支持,另外也有一些朋友提出了不同意见.这其实是很自然的事情,设计本来就充满了各种可能性,没有绝对正确的做法,只有更好的实践.但是设计与实践的好与坏,对于不 ...

随机推荐

  1. NUnit3 Test Adapter vs2015

    NUnit的安装 前言:NUnit是什么? NUnit 是一个单元测试框架,专门针对于.NET来写的.NUnit是xUnit家族种的第4个主打产品,完全由C#语言来编写,并且编写时充分利用了许多.NE ...

  2. IE不能上网、有道云笔记不能联网、各种软件主页不能联网解决办法一

    其他的办法我几乎都试过了,读者可以无搜一下,我的问题是,我用Lantern.exe,所以只要打开这个就可以了! 我一直不知道是这个问题,困扰了好久QAQ

  3. Error opening zip file or JAR manifest missing : D:\play-1.2.5/framework/play-1.2.5.jar

    play框架写的项目,在eclipse中导入.build-path中全部jar包都加入.执行程序,出现: Error occurred during initialization of VM agen ...

  4. Gradle sourceCompatibility has no effect to subprojects(转)

    I have Java 6 and 7 installed on my machine. Gradle uses 1.7 (checked using gradle -v). But I need t ...

  5. Android 动画深入分析

    一些娱乐动画安德鲁斯被广泛使用应用上述的.在不牺牲性能,它可以带来非常好的体验,下面会解释具体的实现安卓动画.知识的学校一个明确清晰的白色. 动画类型 Android的animation由四种类型组成 ...

  6. 安装 CentOS 7 后必做的七件事

    原文 安装 CentOS 7 后必做的七件事 CentOS 是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后, ...

  7. .net程序调用检测和性能分析工具——DotTrace

    DotTrace可以对.net程序进行性能监测,对正在运行的程序和网站监控,主要界面如下: 需要将该工具安装在程序运行的服务器上. 主要用到这个视图,显示了每个方法的时间,下面是反编译出来的代码. P ...

  8. Sicily 1299 Academy Awards (map + vector)集装箱

    链接:http://soj.me/show_problem.php?pid=1299&cid= Description Selected from 3,850 teams from 1,329 ...

  9. 体验VS2015正式版

    初次体验VS2015正式版,安装详细过程.   阅读目录 介绍 安装 介绍    纽约时间7月20日,微软发布了vs 2015 正式版,换算到我们的北京时间就是晚上了,今天回到家里,就下下来了,装上去 ...

  10. 十天学Linux内核之第五天---有关Linux文件系统实现的问题

    原文:十天学Linux内核之第五天---有关Linux文件系统实现的问题 有时间睡懒觉了,却还是五点多醒了,不过一直躺倒九点多才算起来,昨晚一直在弄飞凌的嵌入式开发板,有些问题没解决,自己电脑系统的问 ...