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

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

  在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分层架构之仓储

    DDD分层架构之仓储(层超类型基础篇) 前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操 ...

  3. 应用程序框架实战十五:DDD分层架构之领域实体(验证篇)

    在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石.为了 ...

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

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

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

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

  6. 应用程序框架实战十六:DDD分层架构之值对象(介绍篇)

    前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使用DDD分层架构,但你却从来没有使 ...

  7. 应用程序框架实战十四:DDD分层架构之领域实体(基础篇)

    上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示例代码. 什么是实体 由标识来区分的 ...

  8. 应用程序框架实战三十八:项目示例VS解决方案的创建(一)

    进行项目开发的第一步,是创建出适合自己团队习惯的VS解决方案,虽然我已经提供了项目示例,但毕竟是我创建的,你直接使用可能并不合适,另外你如果尝试模仿重新创建该示例,中间可能碰到各种障碍,特别是项目间的 ...

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

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

随机推荐

  1. POJ 3233Matrix Power Series

    妈妈呀....这简直是目前死得最惨的一次. 贴题目: http://poj.org/problem?id=3233 Matrix Power Series Time Limit: 3000MS Mem ...

  2. Rust语言的多线程编程

    我写这篇短文的时候,正值Rust1.0发布不久,严格来说这是一门兼具C语言的执行效率和Java的开发效率的强大语言,它的所有权机制竟然让你无法写出线程不安全的代码,它是一门可以用来写操作系统的系统级语 ...

  3. python实现最简单的计算器功能源码

    import re def calc(formula): formula = re.sub(' ', '', formula) formula_ret = 0 match_brackets = re. ...

  4. Torch7学习笔记(四)StochasticGradient

    使用随机梯度下降训练神经网络 StochasticGradient是一个比较高层次的类,它接受两个参数,module和criterion,前者是模型结构,后者是损失函数的类型.这个类本身有一些参数: ...

  5. Code of Conduct

    v

  6. Android :fragment介绍

    一.关于Fragmemt 1.Fragment(片段),主要是为了支持更多的动态和灵活的用户界面设计,如平板电脑.Fragment允许组合和交换用户界面组件,而不需要更改视图层次结构.通过把Activ ...

  7. 复习排序with javascript

    最近又翻看了一下数据结构(数据结构学渣). 以前总是看不懂,连冒泡和选择排序都要纠结半天,后来才慢慢有意识能区分开来. 当真的理解了快速排序之后,才觉得,这是个很赞的排序,很容易理解. 于是简单的,模 ...

  8. [IOS]使用了cocoapods 抱错Pods was rejected as an implicit dependency for ‘libPods.a’ because its architectures ......

    Pods was rejected as an implicit dependency for ‘libPods.a’ because its architectures ‘i386’ didn’t ...

  9. bootstrap按钮样式

    <a class='btn' href='javascript:;'>常规按钮</a> <a class='btn btn-small' href='javascript ...

  10. ArcGIS10的附件功能

    转自 积思园 http://blog.csdn.net/linghe301/article/details/6386176 老是忘记怎么使用这个ArcGIS10的附件功能,这次就做个记录吧. 在项目应 ...