前言

简单整理一下工作单元模式。

正文

工作单元模式有3个特性,也算是其功能:

  1. 使用同一上下文

  2. 跟踪实体的状态

  3. 保障事务一致性

工作单元模式 主要关注事务,所以重点在事务上。

在共享层的基础建设类库中加入:

/// <summary>
/// 工作单元接口
/// </summary>
public interface IUnitOfWork : IDisposable
{
/// <summary>
/// 保存变更
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>返回受影响的数据条数</returns>
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default); /// <summary>
/// 保存变更
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>返回保存是否成功</returns>
Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default);
}

SaveChangesAsync 事务第一个影响多少条数

SaveEntitiesAsync 事务是否成功

同样加入事务接口:

interface ITransaction
{
IDbContextTransaction GetCurrentTransaction(); bool HasActiveTransaction { get; } Task<IDbContextTransaction> BeginTransactionAsync(); Task CommitTransactionAsync(IDbContextTransaction transaction); void RollbackTransaction();
}

然后EFContext 实现它们:

/// <summary>
/// EF上下文
/// 注:在处理事务的逻辑部分,需要嵌入CAP的代码,构造函数参数 ICapPublisher
/// </summary>
public class EFContext : DbContext, IUnitOfWork, ITransaction
{
protected IMediator _mediator; ICapPublisher _capBus; public EFContext(DbContextOptions options, IMediator mediator, ICapPublisher capBus)
: base(options)
{
_mediator = mediator;
_capBus = capBus;
} #region IUnitOfWork
/// <summary>
/// 保存实体变更
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{
var result = await base.SaveChangesAsync(cancellationToken); // 执行发送领域事件
await _mediator.DispatchDomainEventsAsync(this); return true;
} ///// <summary>
///// IUniOfWork中该方法的定义与DbContext中的SaveChangesAsync一致,所以此处无需再进行实现
///// </summary>
///// <param name="cancellationToken"></param>
///// <returns></returns>
//public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
//{
// return base.SaveChangesAsync();
//}
#endregion #region ITransaction /// <summary>
/// 当前事务
/// </summary>
private IDbContextTransaction _currentTransaction; /// <summary>
/// 公开方法,返回当前私有事务对象
/// </summary>
/// <returns></returns>
public IDbContextTransaction GetCurrentTransaction() => _currentTransaction; /// <summary>
/// 当前事务是否开启
/// </summary>
public bool HasActiveTransaction => _currentTransaction == null; /// <summary>
/// 开启事务
/// </summary>
/// <returns></returns>
public Task<IDbContextTransaction> BeginTransactionAsync()
{
if (_currentTransaction != null)
{
return null;
}
// 该扩展方法是由CAP组件提供
// 创建事务时,也要把 ICapPublisher 传入
// 核心作用是将我们要发送事件逻辑与我们业务的存储都放在同一个事务内部,从而保证事件与业务逻辑的存取都是一致的
_currentTransaction = Database.BeginTransaction(_capBus, autoCommit: false); return Task.FromResult(_currentTransaction);
} /// <summary>
/// 提交事务
/// </summary>
/// <param name="transaction"></param>
/// <returns></returns>
public async Task CommitTransactionAsync(IDbContextTransaction transaction)
{
if (transaction == null)
{
throw new ArgumentNullException(nameof(transaction));
}
if (transaction != _currentTransaction)
{
throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");
} try
{
// 提交事务之前,安全起见还是要 SaveChanges 一下,保存变更到数据库
await SaveChangesAsync();
transaction.Commit();
}
catch (Exception ex)
{
RollbackTransaction();
throw;
}
finally
{
if (_currentTransaction!=null)
{
_currentTransaction.Dispose();
_currentTransaction = null;
}
}
} /// <summary>
/// 回滚事务
/// </summary>
public void RollbackTransaction()
{
try
{
_currentTransaction?.Rollback();
}
finally
{
if (_currentTransaction!=null)
{
_currentTransaction.Dispose();
_currentTransaction = null;
}
}
}
#endregion }

前面这两个实现了工作单元模式的事务的功能,那么还有一个问题,如何实现管理我们的事务。

/// <summary>
/// 注入事务管理过程
/// </summary>
/// <typeparam name="TDbContext"></typeparam>
/// <typeparam name="TRequest"></typeparam>
/// <typeparam name="TResponse"></typeparam>
public class TransactionBehavior<TDbContext, TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TDbContext : EFContext
{
ILogger _logger;
TDbContext _dbContext;
ICapPublisher _capBus; public TransactionBehavior(TDbContext dbContext, ICapPublisher capBus, ILogger logger)
{
_dbContext = dbContext ?? throw new ArgumentNullException();
_capBus = capBus ?? throw new ArgumentNullException(nameof(capBus));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
} /// <summary>
/// 事务执行
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <param name="next"></param>
/// <returns></returns>
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var response = default(TResponse);
var typeName = request.GetGenericTypeName(); try
{
// 判断当前是否有开启事务,如果开启就执行后续动作
if (_dbContext.HasActiveTransaction)
{
return await next();
} // 数据库操作默认执行策略
// 比如,可以嵌入重试逻辑
var strategy = _dbContext.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async () =>
{
// 开启事务
Guid transactionId;
using (var transaction = await _dbContext.BeginTransactionAsync())
// 记录开启的事务
using (_logger.BeginScope("TransactionContext:{TransactionId}", transaction.TransactionId))
{
_logger.LogInformation("----- 开始事务 {TransactionId} ({@Command})", transaction.TransactionId, typeName, request); // 类似中间件模式,后续逻辑执行完成后,提交事务
response = await next(); _logger.LogInformation("----- 提交事务 {TransactionId} ({CommandName})", transaction.TransactionId, typeName); // 提交事务
await _dbContext.CommitTransactionAsync(transaction); transactionId = transaction.TransactionId; }
}); return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "处理事务出错 {CommandName} ({@Command})", typeName, request);
throw;
} }
}

这里可能会有点疑问,这里没有rollback啊。

using (var transaction = await _dbContext.BeginTransactionAsync())

这一句是托管了,如果中间发生异常,那么会自动调用rollback,using原理前面在c# 基础篇中介绍了,本质就是try catch finnaly这样的模式,这里不详细介绍了。

下一节仓储层的具体实现。

重新整理 .net core 实践篇—————工作单元模式[二十六]的更多相关文章

  1. 重新整理 .net core 实践篇————依赖注入应用[二]

    前言 这里介绍一下.net core的依赖注入框架,其中其代码原理在我的另一个整理<<重新整理 1400篇>>中已经写了,故而专门整理应用这一块. 以下只是个人整理,如有问题, ...

  2. 重新整理 .net core 实践篇—————服务与配置之间[十一二]

    前言 前面基本介绍了,官方对于asp .net core 设计配置和设计服务的框架的一些思路.看下服务和配置之间是如何联系的吧. 正文 服务: public interface ISelfServic ...

  3. 重新整理 .net core 实践篇————polly失败重试[三十四]

    前言 简单整理一下polly 重试. 正文 在开发程序中一般都有一个重试帮助类,那么polly同样有这个功能. polly 组件包: polly 功能包 polly.Extensions.Http 专 ...

  4. 重新整理 .net core 实践篇————配置系统——军令(命令行)[六]

    前言 前文已经基本写了一下配置文件系统的一些基本原理.本文介绍一下命令行导入配置系统. 正文 要使用的话,引入Microsoft.extensions.Configuration.commandLin ...

  5. 重新整理 .net core 实践篇—————日志系统之战地记者[十五]

    前言 本节开始整理日志相关的东西.先整理一下日志的基本原理. 正文 首先介绍一下包: Microsoft.Extengsion.Logging.Abstrations 这个是接口包. Microsof ...

  6. 重新整理 .net core 实践篇—————3种配置验证[十四]

    前言 简单整理一些配置的验证. 正文 配置的验证大概分为3类: 直接注册验证函数 实现IValidteOptions 使用Microsoft.Extensions.Options.DataAnnota ...

  7. 重新整理 .net core 实践篇—————路由和终结点[二十三]

    前言 简单整理一下路由和终节点. 正文 路由方式主要有两种: 1.路由模板方式 2.RouteAttribute 方式 路由约束: 1.类型约束 2.范围约束 3.正则表达式 4.是否必选 5.自定义 ...

  8. 重新整理 .net core 实践篇————配置应用[一]

    前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...

  9. 重新整理 .net core 实践篇—————仓储层的具体实现[二十七]

    前言 简单整理一下仓储层. 正文 在共享层的基础建设类库中: /// <summary> /// 泛型仓储接口 /// </summary> /// <typeparam ...

随机推荐

  1. 【js】Leetcode每日一题-子数组异或查询

    [js]Leetcode每日一题-子数组异或查询 [题目描述] 有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]. 对于每个查询 i ...

  2. 深入源码理解SpringBean生命周期

    概述 本文描述下Spring的实例化.初始化.销毁,整个SpringBean生命周期,聊一聊BeanPostProcessor的回调时机.Aware方法的回调时机.初始化方法的回调及其顺序.销毁方法的 ...

  3. 对ansible不支持service模块的status命令进行修正

    原生的ansible不支持service.status,在Google之后,发现有人提交了一个patch,可以支持status选项.见https://github.com/ritzk/ansible- ...

  4. SSH实现免密登陆

    SSH实现免密登陆配置 ssh实现免密码登录的配置过程,主要分为以下几个步骤: serverA生成密钥,包括私钥和公钥 serverA将公钥传到serverB上 serverA上配置serverB登陆 ...

  5. 查找目录下的所有文件中是否含有某个字符串 find .|xargs grep -ri "IBM"

    linux查看目录下所有文件内容中是否包含某个字符串 2017-07-25 15:13:22 默一鸣 阅读数 21556 文章标签: linux查找文件夹文件内容字符串 更多 分类专栏: Unix   ...

  6. Linux进阶之补充知识篇

    一.Linux系统的主要特点: 开放性:指系统遵循世界标准规范,特别是遵循开放系统互连(OSI)国际标准 多用户:允许多个用户从相同或不同终端上同时使用同一台计算机 多任务:它是指计算机同时执行多个程 ...

  7. Linux中级之lvs三个模式的图像补充(nat,dr,tun)

    负载均衡(Load Balance)集群提供了一种廉价.有效.透明的方法,来扩展网络设备和服务器的负载.带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性. (1)单台计算机无法承受大规 ...

  8. kubectl cmd

    集群资源查看 kubectl get nodes #查看节点状态 kubectl get cs #kubectl检查组件健康状态 kubectl get pods kubectl get all ku ...

  9. SqlServer事务详解(事务隔离性和隔离级别详解)

    概述 不少人对于事务的使用局限于begin transaction:开始事务.commit transaction:提交事务.rollback transaction:回滚事务的初步运用. 并且知道使 ...

  10. Redis学习笔记八:集群模式

    作者:Grey 原文地址:Redis学习笔记八:集群模式 前面提到的Redis学习笔记七:主从复制和哨兵只能解决Redis的单点压力大和单点故障问题,接下来要讲的Redis Cluster模式,主要是 ...