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

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

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

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

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

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

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

  仓储接口IRepository的代码如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Util.Datas;namespace Util.Domains.Repositories {/// <summary>/// 仓储/// </summary>/// <typeparam name="TEntity">实体类型</typeparam>/// <typeparam name="TKey">实体标识类型</typeparam>public interface IRepository<TEntity, in TKey> where TEntity : class, IAggregateRoot<TKey> {/// <summary>/// 添加实体/// </summary>/// <param name="entity">实体</param>void Add( TEntity entity );/// <summary>/// 添加实体/// </summary>/// <param name="entities">实体</param>void Add( IEnumerable<TEntity> entities );/// <summary>/// 修改实体/// </summary>/// <param name="entity">实体</param>void Update( TEntity entity );/// <summary>/// 移除实体/// </summary>/// <param name="id">实体标识</param>void Remove( TKey id );/// <summary>/// 移除实体/// </summary>/// <param name="entity">实体</param>void Remove( TEntity entity );/// <summary>/// 查找实体集合/// </summary>List<TEntity> FindAll();/// <summary>/// 查找实体集合/// </summary>IQueryable<TEntity> Find();/// <summary>/// 查找实体/// </summary>/// <param name="id">实体标识</param>TEntity Find( params object[] id );/// <summary>/// 查找实体列表/// </summary>/// <param name="ids">实体标识列表</param>List<TEntity> Find( IEnumerable<TKey> ids );/// <summary>/// 判断实体是否存在/// </summary>/// <param name="predicate">条件</param>bool Exists( Expression<Func<TEntity, bool>> predicate );/// <summary>/// 索引器查找,获取指定标识的实体/// </summary>/// <param name="id">实体标识</param>TEntity this[TKey id] { get; }/// <summary>/// 保存/// </summary>void Save();/// <summary>/// 获取工作单元/// </summary>
        IUnitOfWork GetUnitOfWork();}
}

using System;namespace Util.Domains.Repositories {/// <summary>/// 仓储/// </summary>/// <typeparam name="TEntity">实体类型</typeparam>public interface IRepository<TEntity> : IRepository<TEntity, Guid> where TEntity : class, IAggregateRoot<Guid> {}
}

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

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using Util.Domains;
using Util.Domains.Repositories;namespace Util.Datas.Ef {/// <summary>/// 仓储/// </summary>/// <typeparam name="TEntity">实体类型</typeparam>/// <typeparam name="TKey">实体标识类型</typeparam>public abstract class Repository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : class, IAggregateRoot<TKey> {/// <summary>/// 初始化仓储/// </summary>/// <param name="unitOfWork">工作单元</param>protected Repository( IUnitOfWork unitOfWork ) {UnitOfWork = (EfUnitOfWork)unitOfWork;}/// <summary>/// Ef工作单元/// </summary>protected EfUnitOfWork UnitOfWork { get; private set; }/// <summary>/// 添加实体/// </summary>/// <param name="entity">实体</param>public void Add( TEntity entity ) {UnitOfWork.Set<TEntity>().Add( entity );UnitOfWork.CommitByStart();}/// <summary>/// 添加实体/// </summary>/// <param name="entities">实体</param>public void Add( IEnumerable<TEntity> entities ) {if ( entities == null )return;UnitOfWork.Set<TEntity>().AddRange( entities );UnitOfWork.CommitByStart();}/// <summary>/// 修改实体/// </summary>/// <param name="entity">实体</param>public virtual void Update( TEntity entity ) {UnitOfWork.Entry( entity ).State = EntityState.Modified;UnitOfWork.CommitByStart();}/// <summary>/// 移除实体/// </summary>/// <param name="id">实体标识</param>public void Remove( TKey id ) {var entity = Find( id );if ( entity == null )return;Remove( entity );}/// <summary>/// 移除实体/// </summary>/// <param name="entity">实体</param>public void Remove( TEntity entity ) {UnitOfWork.Set<TEntity>().Remove( entity );UnitOfWork.CommitByStart();}/// <summary>/// 查找实体集合/// </summary>public List<TEntity> FindAll() {return Find().ToList();}/// <summary>/// 查找实体/// </summary>public IQueryable<TEntity> Find() {return UnitOfWork.Set<TEntity>();}/// <summary>/// 查找实体/// </summary>/// <param name="id">实体标识</param>public TEntity Find( params object[] id ) {return UnitOfWork.Set<TEntity>().Find( id );}/// <summary>/// 查找实体列表/// </summary>/// <param name="ids">实体标识列表</param>public List<TEntity> Find( IEnumerable<TKey> ids ) {if ( ids == null )return null;return Find().Where( t => ids.Contains( t.Id ) ).ToList();}/// <summary>/// 索引器查找,获取指定标识的实体/// </summary>/// <param name="id">实体标识</param>public TEntity this[TKey id] {get { return Find( id ); }}/// <summary>/// 判断实体是否存在/// </summary>/// <param name="predicate">条件</param>public bool Exists( Expression<Func<TEntity, bool>> predicate ) {return Find().Any( predicate );}/// <summary>/// 保存/// </summary>public void Save() {UnitOfWork.Commit();}/// <summary>/// 获取工作单元/// </summary>public IUnitOfWork GetUnitOfWork() {return UnitOfWork;}}
}

using System;
using Util.Domains;namespace Util.Datas.Ef {/// <summary>/// 仓储/// </summary>/// <typeparam name="TEntity">实体类型</typeparam>public abstract class Repository<TEntity> : Repository<TEntity, Guid> where TEntity : class, IAggregateRoot<Guid> {/// <summary>/// 初始化仓储/// </summary>/// <param name="unitOfWork">工作单元</param>protected Repository( IUnitOfWork unitOfWork ): base( unitOfWork ) {}}
}

  仓储是对聚合的操作,所以泛型接口声明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

转载于:https://www.cnblogs.com/xiadao521/p/4169833.html

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

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

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

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

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

  3. 应用程序框架实战三十六:CRUD实战演练介绍

    从本篇开始,本系列将进入实战演练阶段. 前面主要介绍了一些应用程序框架的概念和基类,本来想把所有概念介绍完,再把框架内部实现都讲完了,再进入实战,这样可以让初学者基础牢靠.不过我的精力很有限,文章进度 ...

  4. 应用程序框架实战二十三:基础查询扩展

    上面两篇已经作好准备,本文将进行基础查询扩展.当使用了Entity Framework这样的ORM框架以后,我们查询的核心被集中在IQueryable的Where方法上. 如果UI需要通过姓名查询一个 ...

  5. Android项目实战(二十二):启动另一个APP or 重启本APP

    Android项目实战(二十二):启动另一个APP or 重启本APP 原文:Android项目实战(二十二):启动另一个APP or 重启本APP 一.启动另一个APP 目前公司项目需求,一个主AP ...

  6. 经典C语言程序100例之十二

    经典C语言程序100例之十二 如题 话不多说了,直接上代码 如题 [程序12] 题目:判断101-200之间有多少个素数,并输出所有素数. 1.程序分析:判断素数的方法:用一个数分别去除2到sqrt( ...

  7. 微信小程序把玩(三十二)Image API

    原文:微信小程序把玩(三十二)Image API 选择图片时可设置图片是否是原图,图片来源.这用的也挺常见的,比如个人中心中设置头像,可以与wx.upLoadFile()API使用 主要方法: wx. ...

  8. Linux内核Thermal框架详解十二、Thermal Governor(2)

    本文部分内容参考 万字长文 | Thermal框架源码剖析, Linux Thermal机制源码分析之框架概述_不捡风筝的玖伍贰柒的博客-CSDN博客, "热散由心静,凉生为室空" ...

  9. 程序员编程艺术第一 二十二章集锦与总结(教你如何编程)

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 程序员编 ...

最新文章

  1. ASP.NET的WebFrom组件LinkButton编程
  2. 【★】KMP算法完整教程
  3. Intel Realsense 如何获取已连接所有摄像头的序列号参数?context() query_devices() size() camera_info device_list
  4. 详细解读java IO
  5. Python之路(第三十九篇)管道、进程间数据共享Manager
  6. x264 n-th pass编码时候Stats文件的含义
  7. jquery 找不到live方法解决
  8. 在CentOS 7 上搭建LAMP
  9. java推断字符串是否为乱码
  10. 开源不等于免费!谷歌如何通过安卓开源成为移动时代霸主? | 涛滔不绝
  11. 哈希表---开链法解决哈希冲突
  12. 12 个追地铁的人:照亮生活的一次追逐
  13. This view is not constrained vertically: at runtime it will jump to the left unless you add a vertic
  14. Latex的各种帽子
  15. 算法④:把字符串变成数字
  16. SpringBoot 集成 微信绑定 微信登录
  17. 华为鸿蒙任正非专访,任正非接受专访:华为鸿蒙系统将比安卓速度快60%
  18. Linux安装NLG-Eval
  19. CISSP资料:独家笔记
  20. 秒杀系统的设计与实现(一)

热门文章

  1. 人工智能数学基础-python数值计算实战
  2. php字符串函数的运用,php中字符串比较函数使用方法
  3. 空间滤波_第三章 灰度变换与空间滤波-(六)锐化空间滤波器之拉普拉斯算子...
  4. 小汤学编程之JavaEE学习day04——EL表达式、JSTL
  5. 小汤学编程之JAVA番外篇——Properties工具类
  6. 医疗行业的售前工程师如何撰写人工智能案例申请材料
  7. HTMLCSS常见问题整理(四)
  8. 打造Linux回收站
  9. 回望之六:太阳山与红寺堡
  10. 练习:----点击按钮文字变颜色