应用程序框架实战二十二 : 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工作单元来完成所有的工作。IUnitOfWork和EfUnitOfWork是在应用程序框架实战十九:工作单元层超类型中定义的,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分层架构之仓储(层超类型基础篇)的更多相关文章
- 应用程序框架(一):DDD分层架构:领域实体(基础篇)
一.什么是实体 由标识来区分的对象称为实体. 实体的定义隐藏了几个信息: 两个实体对象,只要它们的标识属性值相等,哪怕标识属性以外的所有属性值都不相等,这两个对象也认为是同一个实体,这意味着两个对象是 ...
- DDD分层架构之仓储
DDD分层架构之仓储(层超类型基础篇) 前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操 ...
- 应用程序框架实战十五:DDD分层架构之领域实体(验证篇)
在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石.为了 ...
- 应用程序框架实战二十一:DDD分层架构之仓储(介绍篇)
前面已经介绍过Entity Framework的工作单元和映射层超类型的封装,从本文开始,将逐步介绍仓储以及对查询的扩展支持. 什么是仓储 仓储表示聚合的集合. 仓储所表现出来的集合外观,仅仅是一种模 ...
- 应用程序框架实战十八:DDD分层架构之聚合
前面已经介绍了DDD分层架构的实体和值对象,本文将介绍聚合以及与其高度相关的并发主题. 我在之前已经说过,初学者第一步需要将业务逻辑尽量放到实体或值对象中,给实体“充血”,这样可以让业务逻辑高度内聚, ...
- 应用程序框架实战十六:DDD分层架构之值对象(介绍篇)
前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使用DDD分层架构,但你却从来没有使 ...
- 应用程序框架实战十四:DDD分层架构之领域实体(基础篇)
上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示例代码. 什么是实体 由标识来区分的 ...
- 应用程序框架实战三十八:项目示例VS解决方案的创建(一)
进行项目开发的第一步,是创建出适合自己团队习惯的VS解决方案,虽然我已经提供了项目示例,但毕竟是我创建的,你直接使用可能并不合适,另外你如果尝试模仿重新创建该示例,中间可能碰到各种障碍,特别是项目间的 ...
- 应用程序框架实战三十六:CRUD实战演练介绍
从本篇开始,本系列将进入实战演练阶段. 前面主要介绍了一些应用程序框架的概念和基类,本来想把所有概念介绍完,再把框架内部实现都讲完了,再进入实战,这样可以让初学者基础牢靠.不过我的精力很有限,文章进度 ...
随机推荐
- C#SerialPort如何读取串口数据并显示在TextBox上
SerialPort中串口数据的读取与写入有较大的不同.由于串口不知道数据何时到达,因此有两种方法可以实现串口数据的读取.一.线程实时读串口:二.事件触发方式实现. 由于线程实时读串口的效率不是十分高 ...
- css border属性做小三角标
<!doctype html><html> <head> <title></title> <meta charset="ut ...
- gulp使用配置
gulpjs是一个前端构建工具,与gruntjs相比,gulpjs无需写一大堆繁杂的配置参数,API也非常简单,学习起来很容易,而且gulpjs使用的是nodejs中stream来读取和操作数据,其速 ...
- 输入/输出系统的四种不同工作方式对CPU利用率比较
程序控制工作方式:输入/输出完全由CPU控制,整个I/O过程中CPU必须等待其完成,因此对CPU的能力限制很大,利用率较低 程序中断工作方式:CPU不再定期查询I/O系统状态,而是当需要I/O处理时再 ...
- 理解MVC,MVP和MVVM设计模式
有3个非常受欢迎的MV-*系列设计模式:MVC,MVP,MVVM.他们被广泛应用于不多种结束.这篇文章我回阐述我自己对这3个设计模式的看法. MVC模式: MVC即Model-VIew-Control ...
- RunLoop
一.什么是RunLoop 从字面意思理解:运行循环.跑圈. 基本作用: 保持程序(应用)的持续运行. 处理程序(APP)中的各种事件(比如:触摸事件.定时事件.Selector事件等) 节省CPU资源 ...
- Filter 数组过滤函数精解示例
'************************************************************************* '**模 块 名:Filter 数组过滤函数精解示 ...
- Underscore-分析
0.Ecmascript的版本重要时间点 Ecmascript262-3 1999年,ie5.5及以后完全支持,ff,chrome等全部支持Ecmascript262-4 因为跨越太大,废弃Ecmas ...
- Ring0打开其他设备对象三种方式整理
1.通过ZwCreateFile打开其他设备的Handle,此函数只能得到句柄.ZwCreateFile与NtCreateFile区别在于NtCreateFile更加底层,ZwCreateFile是基 ...
- Windows驱动开发技术详解HelloWDM例子win7下无法安装
HelloWDM例子编译完成之后,在win7下安装显示 查看setupapi.dev看到如下信息 这个C:\MyDriver_Check目录完全不是我指定的,我放到c盘根目录下 查看inf [Sour ...