DDD领域驱动设计:仓储
1 前置阅读
在阅读本文章之前,你可以先阅读:
- 什么是DDD
- DDD的实体、值对象、聚合根的基类和接口:设计与实现
2 什么是仓储?
仓储封装了基础设施来提供查询和持久化聚合操作。 它们集中提供常见的数据访问功能,从而提供更好的可维护性,并将用于访问数据库的基础结构或技术与领域模型层分离。 创建数据访问层和应用程序的业务逻辑层之间的抽象层。 实现仓储可让你的应用程序对数据存储介质的更改不敏感。
3 为什么仓储?
直接访问数据:
- 重复的代码
- 难以集中化与数据相关的策略(例如缓存)
- 编程错误的可能性更高
- 无法独立于外部依赖项轻松测试业务逻辑
使用仓储优点:
- 可以通过将业务逻辑与数据或服务访问逻辑分开来提高代码的可维护性和可读性。
- 可以从许多位置访问数据源,并希望应用集中管理的,一致的访问规则和逻辑。
- 可以通过自动化进行测试的代码量,并隔离数据层以支持单元测试。
- 可以使用强类型的业务实体,以便可以在编译时而不是在运行时识别问题。
- 可以将行为与相关数据相关联。例如,您要计算字段或在实体中的数据元素之间强制执行复杂的关系或业务规则。
- 应用DDD来简化复杂的业务逻辑。
4 实现仓储?
实现基本的增删改查及事务的提交和回滚
首先,定义接口
/// <summary>
/// IRepository提供应用程序仓储模式基本操作的接口
/// </summary>
public interface IRepository
{
#region Methods
void Entry<T>(T t) where T : AggregateRoot;
void Save();
T Get<T>(Guid id, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot;
T Get<T>(Expression<Func<T, bool>> where, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot;
IQueryable<T> Query<T>(Expression<Func<T, bool>> filter=null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy=null, Func<IQueryable<T>, IQueryable<T>> includes=null) where T : AggregateRoot;
IQueryable<T> QueryByPage<T>(int pageIndex, int pageSize, Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot;
void BeginTransaction();
void Commit();
void Rollback();
#endregion
}
最后,实现以上接口
public class Repository : IDisposable, IRepository
{
#region Private Fields
private readonly DbContext context;
private IDbContextTransaction transaction;
#endregion
#region Constructors
public Repository(DbContext context)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
}
#endregion
#region IRepository<T> Members
public void Entry<T>(T t) where T : AggregateRoot
{
switch (t.AggregateState)
{
case AggregateState.Added:
context.Entry(t).State = EntityState.Added;
break;
case AggregateState.Deleted:
context.Entry(t).State = EntityState.Deleted;
break;
default:
context.Entry(t).State = EntityState.Modified;
break;
}
}
public void Save()
{
context.SaveChanges();
}
public T Get<T>(Guid id, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot
{
return Get(w => w.Id.Equals(id), includes: includes);
}
public T Get<T>(Expression<Func<T, bool>> filter, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot
{
return Query(filter, includes: includes).SingleOrDefault();
}
public IQueryable<T> Query<T>(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot
{
IQueryable<T> query = context.Set<T>();
if (filter != null)
{
query = query.Where(filter);
}
if (includes != null)
{
query = includes(query);
}
if (orderBy != null)
{
query = orderBy(query);
}
return query;
}
public IQueryable<T> QueryByPage<T>(int pageIndex, int pageSize, Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot
{
var query = Query(filter, orderBy, includes)
.Skip(pageSize * (pageIndex - 1))
.Take(pageSize);
return query;
}
public void BeginTransaction()
{
transaction = context.Database.BeginTransaction();
}
public void Rollback()
{
transaction.Rollback();
}
public void Commit()
{
transaction.Commit();
}
public void Dispose()
{
if (transaction != null)
{
transaction.Dispose();
}
context.Dispose();
}
#endregion
}
为数据库上下文和事务上下文声明类变量:
private readonly DbContext context;
private IDbContextTransaction transaction;
构造函数接受数据库上下文实例:
public Repository(DbContext context)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
}
Get分为通过ID查询或过滤条件进行查询,返回序列中的唯一元素:
public T Get<T>(Guid id, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot
{
return Get(w => w.Id.Equals(id), includes: includes);
}
public T Get<T>(Expression<Func<T, bool>> filter, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot
{
return Query(filter, includes: includes).SingleOrDefault();
}
Query 方法使用 lambda 表达式来允许调用代码指定筛选条件,使用一列来对结果进行排序,允许调用方为预先加载导航属性列表:
// 代码 Expression<Func<T, bool>> filter 意味着调用方将基于 AggregateRoot 类型提供 lambda 表达式,并且此表达式将返回一个布尔值。
// 代码 Func<IQueryable<T>, IOrderedQueryable<T>> orderBy 也意味着调用方将提供 lambda 表达式。 但在这种情况下,表达式的输入是 AggregateRoot 类型的 IQueryable 对象。 表达式将返回 IQueryable 对象的有序版本。
// 代码 Func<IQueryable<T>, IQueryable<T>> includes 也意味着调用方将提供 lambda 表达式。 允许预先加载导航属性列表。
public IQueryable<T> Query<T>(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, Func<IQueryable<T>, IQueryable<T>> includes = null) where T : AggregateRoot
{
IQueryable<T> query = context.Set<T>();
if (filter != null)
{
query = query.Where(filter);
}
if (includes != null)
{
query = includes(query);
}
if (orderBy != null)
{
query = orderBy(query);
}
return query;
}
Entry 方法使用 AggregateRoot.AggregateState 来置 context.Entry(t).State 状态,完成增删改
public void Entry<T>(T t) where T : AggregateRoot
{
switch (t.AggregateState)
{
case AggregateState.Added:
context.Entry(t).State = EntityState.Added;
break;
case AggregateState.Deleted:
context.Entry(t).State = EntityState.Deleted;
break;
default:
context.Entry(t).State = EntityState.Modified;
break;
}
}
Save 等其他方法也类似实现。
DDD领域驱动设计:仓储的更多相关文章
- DDD领域驱动设计仓储Repository
DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repositor ...
- C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)
前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...
- C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)
前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...
- DDD领域驱动设计初探(二):仓储Repository(上)
前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...
- DDD领域驱动设计初探(三):仓储Repository(下)
前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...
- 浅谈我对DDD领域驱动设计的理解
从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...
- DDD领域驱动设计之领域服务
1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 3.DDD领域驱动设计之领域基础设施层 什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B ...
- DDD 领域驱动设计-三个问题思考实体和值对象(续)
上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...
- DDD 领域驱动设计-看我如何应对业务需求变化,领域模型调整?
写在前面 上一篇:DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对? "愚蠢的应对",这个标题是我后来补充上的,博文中除了描述需求变化.愚蠢应对和一些思考,确实没有实质性 ...
- C#进阶系列——DDD领域驱动设计初探(一):聚合
前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...
随机推荐
- 总结JAVA语言的十大特性
JAVA语言的十大特性 1.简单 Java语言的语法简单明了,容易掌握从,而且Java语言是纯面向对象的语言. Java语言的语法规则和C++类似,从某种意义上来讲,Java原因是由C语言和C++语言 ...
- ArrayListHashmap嵌套
package arrayListHashMap; import java.util.ArrayList; import java.util.HashMap; import java.util.Map ...
- Qt类库介绍
QT类库 QT核心特点 QT是一个跨平台开发的类库. QT的元对象编译器MOC是一个预处理器,在源程序被编译前先将这些QT特性的程序转为标准的C++兼容的形式,然后再有标准的C++编译器进行编译.也就 ...
- Socket粘包问题的3种解决方案,最后一种最完美!
在 Java 语言中,传统的 Socket 编程分为两种实现方式,这两种实现方式也对应着两种不同的传输层协议:TCP 协议和 UDP 协议,但作为互联网中最常用的传输层协议 TCP,在使用时却会导致粘 ...
- 深入理解Kafka必知必会(1)
Kafka的用途有哪些?使用场景如何? 消息系统: Kafka 和传统的消息系统(也称作消息中间件)都具备系统解耦.冗余存储.流量削峰.缓冲.异步通信.扩展性.可恢复性等功能.与此同时,Kafka 还 ...
- memcached的安装教程
在windows系统上安装memcached 下载安装软件memcached-1.2.6-win32-bin.zip 解压该文件把memcached.exe 拷贝到你的 apache同一目录 安装该m ...
- MySQL 集群知识点整理
随着项目架构的不断扩大,单台 MySQL 已经不能满足需要了,所以需要搭建集群将前来的请求进行分流处理.博客主要根据丁奇老师的专栏<<MySQL实战45讲>>学习的总结. 架构 ...
- (数据科学学习手札103)Python+Dash快速web应用开发——页面布局篇
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- 【Java】集合综合案例 - 播放器管理
集合综合案例 文章目录 集合综合案例 需求分析 项目演示 详细设计 代码实现 歌曲类 播放器类 播放列表类 测试 参考资料 播放器管理 需求分析 项目演示 详细设计 代码实现 重新搞一波 复习巩固 简 ...
- 【Linux】rsh进程缓慢问题处理
环境CentOS 6.5 由于项目上线时间很长,服务器持续很久没有关机重启过,随后发现rsh反应特别慢 rsh登陆服务器的反应最慢时候3分钟才可以建立链接,登陆之后查看服务器负载是否正常,查看cpu, ...