应用程序框架实战二十二 : 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

(0)

相关推荐

  • .NetCore学习笔记:二、基于Dapper的泛型Repository

    为减少代码量,这里实现一个基于Dapper的泛型Repository. 这里需要引用Dapper.dll和Dapper.Contrib.dll. 接口定义: 1 /// <summary> ...

  • 二十二式魔杖 实战棍法

    二十二式魔杖 实战棍法

  • 抄底,逃顶 实战各十二招

    ​​​ ​​​转自好过些  实战逃顶十二招:第一招"顶部背离" ​ 实战逃顶十二招:第二招"双阴守护" ​ 实战逃顶十二招:第三招"巨量大阴" ...

  • 新手实战二十二:波浪理论实战用法

    波浪理论的基础学习,以及如何通过计算每个浪形的点位来参考买卖点.

  • 图解 | 抄底逃顶 实战各十二招

    抄底第一招:背离共振 短线投机还是长线投资大家都想买在阶段性的低位,买在阶段的高位赚钱差价,抄底是大家共同最求的目标,但是底部在那,如何抄底,成为股票大众投资者的难题,在那抄底,如何抄底,让中国股市实 ...

  • 实战 I 十二年操盘手总结的十大操盘利器

    实战 I 十二年操盘手总结的十大操盘利器

  • 十万个冷知识(四百二十二)

    挪威很多父母又坚新鲜空气下睡眠有益健康!于是,从两岁开始,挪威的娃娃们就开始在户外休憩,公立托儿所也会把小孩放在户外露天午睡.即使气温是-5摄氏度!搁在咱大中国,这爹妈早被爷爷奶奶打残了! " ...

  • 二十二年了,不能忘

    有些人.有些事 不能忘 也不敢忘 22年前的今天 北京时间1999年5月8日 清晨5时45分 以美国为首的北约悍然轰炸 我驻南斯拉夫联盟共和国大使馆 导弹划破漆黑的夜空 几声惊天的巨响 大使馆顷刻间火 ...

  • 人生中心情驿站二十二(之十二)

    徜徉在知识的海洋而不觉得自己渺小  劈波于历史的长河贯看天地秋月春风 --------中国摄像师潘长宏题 心情驿站之十二 1.一切一切都在变,只有人性没有变. 2.家庭教育决定了一个人的基本修养. 3 ...

  • 偏方大本营,每天二十方(之二十二)

    偏方大本营(之二十二) 1:治宫颈癌白英.土茯苓.苦参.鸡冠花.半枝莲.墓头回各12克.水煎服,每日1剂.带下加白槿花.糯根皮.白鸡冠花各12克. 2:治顽固性失眠薏苡仁60克,半夏.夏枯草.茯神.生 ...