.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 IUnitOfWorkpublic 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 ITransactionprivate 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 来实现我们的仓储层

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310989.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《C++ Primer》7.3.2节练习

练习7.27: #include <iostream> #include <cstring> using namespace std;class Screen {private:unsigned height 0, width 0;unsigned cursor 0;string contents;public:Screen() default;Screen(unsigned ht, unsigned wd): height(ht), width(wd), conten…

【实战 Ids4】║ 控制台密码模式搭配Ocelot网关

&#xff08;此岁只能云赏樱了&#xff09;书接上文&#xff0c;这些天一直在研究IdentityServer4&#xff08;下文简称Ids4&#xff09;框架&#xff0c;发现有很多有意思&#xff0c;或者说比我想象中的知识点&#xff0c;可扩展的多&#xff0c;所以比较开心能钻研进去&…

mysql递归查询 缓存_MySQL-递归查询方法解析

MySQL-递归查询方法解析&#xff0c;兄弟连教育(www.lampbrother.net)帮大家做了个简单的整理&#xff1a;有需要的朋友可以参考下哈。首先表结构和数据CREATE TABLE class ( classid int(11) NOT NULL AUTO_INCREMENT, banji int(11) DEFAULT NULL COMMENT 0, nianji varch…

《C++ Primer》7.3.3节练习

练习7.31: 满足题意的程序如下所示&#xff1a; class X;//声明类型X class Y//定义类型Y {X* x; }; class X//定义类型X {Y y; };类X的声明称为前向声明&#xff0c;它向程序中引入了名字X并且指明X是一种类类型。对于类型X来说&#xff0c;此时我们已知它是一个类类型&#…

.NET Core 如何生成信用卡卡号

点击上方蓝字关注“汪宇杰博客”导语上个月我写了《.NET Core 如何验证信用卡卡号》&#xff0c;不少朋友表示挺有兴趣。在金融科技行业的实际工作中&#xff0c;通常还需要生成信用卡卡号用来测试&#xff0c;今天我就来教大家如何生成信用卡卡号。上回的改进上篇文章写完后&a…

python怎么爬虎牙_使用python爬虫框架scrapy抓取虎牙主播数据

前言本文利用python的scrapy框架对虎牙web端的主播、主播订阅数、主播当前观看人数等基本数据进行抓取&#xff0c;并将抓取到的数据以csv格数输出&#xff0c;以及存储到mongodb中思路观察虎牙网站后确认所有频道url都在www.huya.com/g中的&#xff0c;而主播房间数据则是ajax…

《C++ Primer》7.3.4节练习

练习7.32: 要想让clear函数作为Screen的友元&#xff0c;只需要在Screen类中做出友元声明即可。本题的真正关键之处是程序的组织结构&#xff0c;我们必须首先定义Window_mgr类&#xff0c;其中声明clear函数&#xff0c;但是不能定义它&#xff1b;接下来定义Screen类&#xf…

.NET Core开发实战(第29课:定义仓储:使用EF Core实现仓储层)--学习笔记

29 | 定义仓储&#xff1a;使用EF Core实现仓储层首先定义仓储层的接口&#xff0c;以及仓储层实现的基类&#xff0c;抽象类仓储层的接口namespace GeekTime.Infrastructure.Core {/// <summary>/// 包含普通实体的仓储/// 约束 TEntity 必须是继承 Entity 的基类&#…

ueditor单图上传iframe跨域_UEditor单图上传(simpleupload)跨域问题解决方案

代码实现首先我们需要在ueditor.all.js文件中找到原本的单图上传部分的代码搜索关键字 simpleupload&#xff0c;如下图所示&#xff1a;然后找到上传图片的代码片段&#xff0c;如下图所示&#xff1a;然后把 domUtils.on的 input 绑定的事件注释掉或删除掉替换成以下代码:inp…

StringBuilder内存碎片对性能的影响

TL;DR:StringBuilder内部是由多段 char[]组成的半自动链表&#xff0c;因此频繁从中间修改 StringBuilder&#xff0c;会将原本连续的内存分隔为多段&#xff0c;从而影响读取/遍历性能。连续内存与不连续内存的性能差&#xff0c;可能高达 1600倍。背景用 StringBuilder的用户…

《C++ Primer》7.4节练习

练习7.33: 题目代码&#xff1a; pos Screen::size()const {return height*width; }如果添加如题目所示的size函数将会出现编译错误。因为该函数的返回类型pos本身定义在Screen类的内部&#xff0c;所以在类的外部无法直接使用pos。要想使用pos&#xff0c;需要在它的前面加上…

java 双击_利用java开发一个双击执行的小程序

之前我们利用java写了很多东西&#xff0c;但是好像都没有什么实际意义。因为有意义桌面小程序怎么都得有个界面&#xff0c;可是界面又不太好搞。或者 了解到这一层的人就少之又少了。呀&#xff0c;是不是还得开辟一些版面来介绍awt和 swing。。。算了 先把这个 双击执行的小…

开发人员如何学习 Kubernetes

虽然“容器编排平台”还没有被整个行业大范围采用&#xff0c;但在这一领域 Kubernetes 已经战胜其他选手&#xff0c;成为了事实标准。近两年的 Web 开发技术社区&#xff0c;随便打开一两个群&#xff0c;你都能看到人们在谈 Kubernetes。很多开发人员&#xff0c;包括曾经的…

《C++ Primer》7.5.1节练习

练习7.36: 在类X中&#xff0c;两个数据成员出现的顺序是rem在前&#xff0c;base在后&#xff0c;所以当执行X对象的初始化操作时先初始化rem。如上述代码所示&#xff0c;初始化rem要用到base的值&#xff0c;而此时base尚未被初始化&#xff0c;因此会出现错误。该过程与构造…

安装 java decompiler_Eclipse离线安装Java Decompiler插件(反编译)

Java Decompiler是Java语言的反编译工具&#xff0c;具体介绍见博客Java Decompiler(Java反编译工具)1、下载插件Eclipe的Java Decompiler插件名为JD-Eclipse&#xff0c;2、安装插件Ecipse安装JD-Eclipse(即Java Decompiler)插件步骤如下&#xff1a;打开Help --> Install …

给 ABP vNext 应用安装私信模块

在上一节五分钟完成 ABP vNext 通讯录 App 开发 中&#xff0c;我们用完成了通讯录 App 的基础开发。这本章节&#xff0c;我们会给通讯录 App 安装私信模块&#xff0c;使不同用户能够通过相互发送消息&#xff0c;并接收新私信的通知。在章节的最后&#xff0c;笔者将演示模块…

《C++ Primer》7.5.2节练习

练习7.41: #include <iostream> #include <string> using namespace std;class Sales_data {friend std::istream &read(std::istream &is, Sales_data &item);friend std::ostream &print(std::ostream &os, const Sales_data &item);pu…

零基础玩视频号?创作运营变现,你要的干货都在这了!

点击蓝字“大白技术控”关注我哟加个“星标★”&#xff0c;每日良时&#xff0c;好文必达&#xff01;不少小伙伴应该已经听说过视频号这个新功能了&#xff0c;视频号是微信内测的短视频功能&#xff0c;本人已经在视频号里刷了2个月了。3月中旬正式开通了视频号 「大白技术控…

《C++ Primer》14.1节练习

练习14.1: 不同点&#xff1a; 重载操作符必须具有至少一个class或枚举类型的操作数。 重载操作符不保证操作数的求值顺序&#xff0c;例如对&&和||的重载版本不再具有“短路求值”的特性&#xff0c;两个操作数都要进行求值&#xff0c;而且不规定操作数的求值顺序。 …

mysql 磁盘i o 优化_经典案例:磁盘I/O巨高排查全过程

作者&#xff1a;叶金荣&#xff0c;知数堂联合创始人&#xff0c;3306pai社区联合创始人前言是什么原因导致线上数据库服务器磁盘I/O的util和iowait持续飚高&#xff1f;1. 问题描述朋友小明的线上数据库突发严重告警&#xff0c;业务方反馈写入数据一直堵住&#xff0c;很多锁…