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

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

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

  在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

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

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. Android4.0 Design之UI设计缺陷1

    我想成为Android卓越发展project联赛,不知道Android它如何设计规则,Android4.0谷歌公司的问世后Android一系列的设计原则,程序猿规范,不要盲目模仿IOS它的设计,由于A ...

  2. js 性能优化整理之 惰性载入

    跨检查浏览器特性,解决不同浏览器的兼容问题. 例如,我们最常见的为 dom 节点添加事件的函数 function addEvent(element,type,handler){ if(element. ...

  3. 介绍 Microservice

    介绍 Microservice 这篇文章转自我的 Github blog 一天我司招财猫姐(HR 大人)问我,你给我解释一下 Microservice 是什么吧.故成此文.一切都是从一个创业公司开始的 ...

  4. 单机部署redis主从备份

    redis为了避免单点故障,也支持主从备份.个人在做主从备份的实验时,因为机器数量有限,一般非常少有多台机器做支撑. 本文就将叙述怎样在一台电脑上实现redis的主从备份. 同一台机器上部署多个red ...

  5. hibernate 单元測试框架

    hibernate在写数据库配置文件时很的不确定,必须进行必要的測试保证数据库结构的正确性.所以能够应用junit进行測试. 使用junit很easy,eclipse仅仅须要右键项目新建一个junit ...

  6. hdu4288 Coder 2012成都网络赛 A题

    题意:往集合里面添加删除数,集合中的数是按从小到大排列的,询问下标模5等于3的数的和. 记得当时这题不会做, 现在想简单多了,只要维护五个值和左右子树的size大小就行了. #define maxn ...

  7. Spring3+SpingMVC+Hibernate4全注解环境配置

    Spring3+SpingMVC+Hibernate4全注解环境配置 我没有使用maven,直接使用Eclipse创建动态Web项目,jar包复制在了lib下.这样做导致我马上概述的项目既依赖Ecli ...

  8. poj3417 Network 树形Dp+LCA

    题意:给定一棵n个节点的树,然后在给定m条边,去掉m条边中的一条和原树中的一条边,使得树至少分为两部分,问有多少种方案. 神题,一点也想不到做法, 首先要分析出加入一条边之后会形成环,形成环的话,如果 ...

  9. DEV GridView嵌套

    近来的DEV搞更多.试图寻找专业点的程序做,对这样一个小小的研究. 本篇是多么真实,现在的记录,可以通过点击这条线的子表的内容相关联的行中打开的列表.的影响,如下面的: 以下是实现过程: 1.设计器里 ...

  10. vimer

    vimer 第1.0章.统一概念   不管学什么技术,我都深信概念是最重要的.是影响整个学习轨迹,决定能在这个技术领域高度. 当然如果你现在的目的不是在学习而在于解决问题(很多人不愿意承认,或者没发现 ...