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. Centos7 根目录存储空间扩展方法

    Centos7 根目录存储空间扩展方法   一.首先通过 df -hl 命令查看磁盘占用情况,其中根目录已经被占满,此时需要对其进行扩容   二.针对虚拟机环境的centos7系统根存储空间扩容,可利 ...

  2. java中使用IO流复制文件

    public class TestIO { public static void main(String[] args) { // TODO Auto-generated method stub tr ...

  3. Promise是如何实现异步编程的?

    Promise标准 不能免俗地贴个Promise标准链接Promises/A+.ES6的Promise有很多方法,包括Promise.all()/Promise.resolve()/Promise.r ...

  4. C# ——获取各国时间

    DateTime dt = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now, TimeZoneInfo.Local); DateTime dt1 = TimeZo ...

  5. node2vec实现源码详解

    一.按照程序执行的顺序,第一步是walker.py中的preprocess_transition_probs()函数 这个函数的作用是生成两个采样预备数据,alias_nodes,alias_edge ...

  6. 风炫安全web安全学习第三十节课 命令执行&代码执行基础

    风炫安全web安全学习第三十节课 命令执行&代码执行基础 代码执行&命令执行 RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统. 远程系统命令执行 ...

  7. OpenOCD安装与使用(JTAG调试)

    本文介绍openocd开源软件的安装以及搭配JTAG对Xilinx u500VC707devkit的调试 PC OS: Ubuntu20.04 LTS Target ARCH: riscv64 JTA ...

  8. 【Azure Developer】使用Postman获取Azure AD中注册应用程序的授权Token,及为Azure REST API设置Authorization

    Azure Active Directory (Azure AD) is Microsoft's cloud-based identity and access management service, ...

  9. selenium自动化 | 实现抢课功能

    # -*- coding: utf-8 -*-"""Created on Wed Jan 1 23:39:34 2020@author: billie程序运行环境要求:- ...

  10. 如何使用github搜索需要的开源项目

    按照项目名/仓库名搜索(大小写不敏感)in:name xxx按照README搜索(大小写不敏感)in:readme xxx按照description搜索(大小写不敏感)in:description x ...