.NET Core开发实战(第28课:工作单元模式(UnitOfWork):管理好你的事务)--学习笔记
28 | 工作单元模式(UnitOfWork):管理好你的事务
工作单元模式有如下几个特性:
1、使用同一上下文
2、跟踪实体的状态
3、保障事务一致性
我们对实体的操作,最终的状态都是应该如实保存到我们的存储中,进行持久化
接下来看一下代码
为了实现工作单元模式,这里定义了一个工作单元的接口
public interface IUnitOfWork : IDisposable
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default);
}
这两个方法的区别是:一个是返回的 int 是指我们影响的数据条数,另外一个返回 bool 表示我们保存是否成功,本质上这两个方法达到的效果是相同的
另外还定义了一个事务管理的接口
public interface ITransaction
{
// 获取当前事务
IDbContextTransaction GetCurrentTransaction();
// 判断当前事务是否开启
bool HasActiveTransaction { get; }
// 开启事务
Task<IDbContextTransaction> BeginTransactionAsync();
// 提交事务
Task CommitTransactionAsync(IDbContextTransaction transaction);
// 事务回滚
void RollbackTransaction();
}
在实现上我们是借助 EF 来实现工作单元模式的
看一下 EFContext 的定义
/// <summary>
/// DbContext 是 EF 的基类,然后实现了 UnitOfWork 的接口和事务的接口
/// </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
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{
var result = await base.SaveChangesAsync(cancellationToken);
//await _mediator.DispatchDomainEventsAsync(this);
return true;
}
//// 可以看到这个方法实际上与上面的方法是相同的,所以这个方法可以不实现
//public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
//{
// return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
//}
#endregion
#region ITransaction
private IDbContextTransaction _currentTransaction;// 把当前的事务用一个字段存储
public IDbContextTransaction GetCurrentTransaction() => _currentTransaction;// 获取当前的事务就是返回存储的私有对象
public bool HasActiveTransaction => _currentTransaction != null;// 事务是否开启是判断当前这个事务是否为空
/// <summary>
/// 开启事务
/// </summary>
/// <returns></returns>
public Task<IDbContextTransaction> BeginTransactionAsync()
{
if (_currentTransaction != null) return null;
_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
{
await SaveChangesAsync();// 将当前所有的变更都保存到数据库
transaction.Commit();
}
catch
{
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
}
另外一个我们还是需要关注的一点就是如何管理我们的事务
这里有一个类 TransactionBehavior,这个类是用来注入我们的事务的管理过程的,具体它是怎么工作的在后续的章节会讲到,这里先关注它的实现过程
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(nameof(dbContext));
_capBus = capBus ?? throw new ArgumentNullException(nameof(capBus));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
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();// 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;
}
}
}
回过头来看一下我们的 EFContext,EFContext 实现 IUnitOfWork,工作单元模式的核心,它实现了事务的管理和工作单元模式,我们就可以借助 EFContext 来实现我们的仓储层
GitHub源码链接:
https://github.com/witskeeper/geektime



本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。
.NET Core开发实战(第28课:工作单元模式(UnitOfWork):管理好你的事务)--学习笔记的更多相关文章
- 仓储(Repository)和工作单元模式(UnitOfWork)
仓储和工作单元模式 仓储模式 为什么要用仓储模式 通常不建议在业务逻辑层直接访问数据库.因为这样可能会导致如下结果: 重复的代码 编程错误的可能性更高 业务数据的弱类型 更难集中处理数据,比如缓存 无 ...
- 工作单元模式(UnitOfWork)学习总结
工作单元的目标是维护变化的对象列表.使用IUnitOfWorkRepository负责对象的持久化,使用IUnitOfWork收集变化的对象,并将变化的对象放到各自的增删改列表中, 最后Commit, ...
- .NET Core MongoDB数据仓储和工作单元模式封装
前言 上一章我们把系统所需要的MongoDB集合设计好了,这一章我们的主要任务是使用.NET Core应用程序连接MongoDB并且封装MongoDB数据仓储和工作单元模式,因为本章内容涵盖的有点多关 ...
- 2月送书福利:ASP.NET Core开发实战
大家都知道我有一个公众号“恰童鞋骚年”,在公众号2020年第一天发布的推文<2020年,请让我重新介绍我自己>中,我曾说到我会在2020年中每个月为所有关注“恰童鞋骚年”公众号的童鞋们送一 ...
- .NET Core MongoDB数据仓储和工作单元模式实操
前言 上一章节我们主要讲解了MongoDB数据仓储和工作单元模式的封装,这一章节主要讲的是MongoDB用户管理相关操作实操.如:获取所有用户信息.获取用户分页数据.通过用户ID获取对应用户信息.添加 ...
- Asp.Net Core 项目实战之权限管理系统(6) 功能管理
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- 第二篇 :微信公众平台开发实战Java版之开启开发者模式,接入微信公众平台开发
第一部分:微信公众号对接的基本介绍 一.填写服务器配置信息的介绍 登录微信公众平台官网后,进入到公众平台后台管理页面. 选择 公众号基本设置->基本配置 ,点击“修改配置”按钮,填写服务器地址( ...
- [.NET领域驱动设计实战系列]专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了 ...
- [.NET领域驱动设计实战系列]专题四:前期准备之工作单元模式(Unit Of Work)
一.前言 在前一专题中介绍了规约模式的实现,然后在仓储实现中,经常会涉及工作单元模式的实现.然而,在我的网上书店案例中也将引入工作单元模式,所以本专题将详细介绍下该模式,为后面案例的实现做一个铺垫. ...
- 重新整理 .net core 实践篇—————工作单元模式[二十六]
前言 简单整理一下工作单元模式. 正文 工作单元模式有3个特性,也算是其功能: 使用同一上下文 跟踪实体的状态 保障事务一致性 工作单元模式 主要关注事务,所以重点在事务上. 在共享层的基础建设类库中 ...
随机推荐
- js根据对象数组中某一属性值,合并相同项,并对某一属性累加处理
https://www.cnblogs.com/mahao1993/p/13491430.html
- P1064-DP【绿】
好多好多天前写了这道题的50分代码,然后不知道错在哪里反复调没调对.然后这周我极度忙,忙死了,好不容易有一点时间再来审视这道题了,然后我5分钟想明白了一切...意识到自己此前的错误有多弱智... 把D ...
- java基础数据类型-int类型-浮点型-数据类型的转换--day02
目录 1. 变量的命名 2. 常量 3. 变量 4. 进制 4.1 进制转换 4.2 整型数据类型 5 浮点型 6. 字符串 7. 整形与字符型之间的转换 8 最常见的一个乱码问题 9. 布尔类型 1 ...
- 在Chrome中安装扩展程序
场景:在Chrome中安装NetBeans Connector插件,将下载好的crx文件拖到扩展程序页面时,发现该插件并没有安装成功. 分析:浏览器默认禁用了拖入安装 .crx 扩展的功能,导致crx ...
- docker 安装 ElasticSearch 和 Kibana 及ik 中文分词器
本文为博主原创,未经允许不得转载: 1. 使用 docker 下载 elasticsearch 7.6.1 docker pull elasticsearch:7.6.1 2. 启动 elastic ...
- 基于html+jquery开发的科学计算器(课程作业)
基于html和jquery开发的科学计算器,该科学计算器可进行乘方.开方.指数.对数.三角函数.统计等方面的运算,又称函数计算器. 科学型带有所有普通的函数,所有的函数都分布在键盘上以致于你可以不用通 ...
- [转帖]SQL SERVER DBCC命令详解
https://developer.aliyun.com/article/867768 简介: SQL数据库开发 DBCC DROPCLEANBUFFERS:从缓冲池中删除所有缓存,清除缓冲区 在 ...
- [转帖]win10多网卡指定ip走某个网卡的方案
https://zhuanlan.zhihu.com/p/571614314 我的电脑上有两个网卡,一个网卡A(网线),一个是网卡B(WIFI). 需求:网卡A和网卡B是不同的网络,网卡A已经把338 ...
- mysql 的outfile以及infile 语法简单备份恢复表
今天学习 姜老师的 mysql技术内存innodb存储引擎时看到了有一个outfile和infile的语法,感觉挺好的, 这里面进行一下简单的学习与验证. oufile 可以到处文件 infile 可 ...
- 你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队
1.前言 不要犹豫了,GC最大停顿时间小于1ms,支持16TB内存,这么高的性能提升,也不需要复杂的调优,节省了这个时间,你去陪对象不香嘛. 上篇文章给大家带来了JDK11升级JDK17的最全实践,相 ...