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领域驱动设计:仓储的更多相关文章

  1. DDD领域驱动设计仓储Repository

    DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repositor ...

  2. C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

  3. C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)

    前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...

  4. DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

  5. DDD领域驱动设计初探(三):仓储Repository(下)

    前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...

  6. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  7. DDD领域驱动设计之领域服务

    1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 3.DDD领域驱动设计之领域基础设施层 什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B ...

  8. DDD 领域驱动设计-三个问题思考实体和值对象(续)

    上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...

  9. DDD 领域驱动设计-看我如何应对业务需求变化,领域模型调整?

    写在前面 上一篇:DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对? "愚蠢的应对",这个标题是我后来补充上的,博文中除了描述需求变化.愚蠢应对和一些思考,确实没有实质性 ...

  10. C#进阶系列——DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

随机推荐

  1. Omega System Trading and Development Club内部分享策略Easylanguage源码

    更多精彩内容,欢迎关注公众号:数量技术宅.关于本期分享的任何问题,请加技术宅微信:sljsz01 关于 Omega System Trading and Development Club " ...

  2. 阿里巴巴java开发手册-嵩山版 下载

    引言 今天阿里巴巴开发手册嵩山版又发布了,距离上次泰山版发布才仅仅几个月.是不是有的同学又要感叹下这速度也太快了点吧.我泰山版还没看完,嵩山版直接来了.没看完不要紧,我们直接看嵩山版本就好了.一次性把 ...

  3. OJDBC版本区别

    classes12.jar,ojdbc14.jar,ojdbc5.jar和ojdbc6.jar,ojdbc7.jar的区别与差异 [转 原文:https://yq.aliyun.com/wenji/2 ...

  4. Qt学习笔记-界面设置入门

    首先我们看的是qt类表.相当于是一个族谱.这个文档可以在网上搜到. 首先第一个是QObjiect. 然后是QWidget => 相当于是windowsget缩写. 同理: QColorDialo ...

  5. Jmeter(三十四) - 从入门到精通进阶篇 - 参数化(详解教程)

    1.简介 前边三十多篇文章主要介绍的是Jmeter的一些操作和基础知识,算是一些初级入门的知识点,从这一篇开始我们就来学习Jmeter比较高级的操作和深入的知识点了.今天这一篇主要是讲参数化,其实前边 ...

  6. SpringBoot整合sa-token,完成网站权限验证

    sa-token是什么? sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要: // 在登录时写入当前会话的账号id StpUtil.s ...

  7. 最实用的visual studio插件,值得收藏!

    1.ReSharper(VS必装,代码重构.代码修正功能) ReSharper 是一个JetBrains公司出品的著名的代码生成工具.其能帮助Microsoft Visual Studio成为一个更佳 ...

  8. Redis缓存篇(四)缓存异常

    这一节,我们来学习一下缓存异常.缓存异常有四种类型,分别是缓存和数据库的数据不一致.缓存雪崩.缓存击穿和缓存穿透. 下面通过了解这四种缓存异常的原理和应对方法. 缓存和数据库的数据不一致 缓存和数据库 ...

  9. Nginx解决前端访问资源跨域问题

    被前端跨域问题折磨快2天后,终于用ngnx的方式解决了,所以在此总结下. 该篇只探讨如何用Ngnx解决跨域问题,对于原理不作讨论. 1.首先介绍Windows环境下Nignx的相关命令操作 nginx ...

  10. ForwardProcess

    共享式网络中,发送的报文所有设备都可以收到 0.0.0.0/0 缺省路由 可以到达任意网络 From WizNote