前言

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

正文

工作单元模式有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. 矩阵旋转-Eigen应用(QTCreator编辑器)

    * { font-family: "Tibetan Machine Uni", "sans-serif", STFangSong; outline: none ...

  2. NtQuerySystemInformation获取进程/线程状态

    __kernel_entry NTSTATUS NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, P ...

  3. centos 7.6 安装最新版docker 19.03

    systemctl stop docker rpm -qa | grep docker 看到那个删除那个yum erase docker \ docker-client \ docker-client ...

  4. 在?开源社区版的 AirTag 请收下——GitHub 热点速览 v.21.21

    作者:HelloGitHub-小鱼干 在比特币跌到怀疑人生的时候,看着"出血不止"的荷包,是时候来"薅"一波羊毛了.openhaystack 能让你免去购买 A ...

  5. vmware安装ubuntu ,一直处于end kernel panic - not syncing : corrupted stack end detected inside scheduler

    vmware安装ubuntu ,一直处于end kernel panic - not syncing : corrupted stack end detected inside scheduler y ...

  6. Linux创建RAID0_实战

    Linux创建RAID实战 一.Linux创建RAID0 RAID0俗称条带,它将两个或多个硬盘组成一个逻辑硬盘,容量是所有硬盘之和 因为是多个硬盘组合成一个,故可并行写操作,写入速度提高,但此方式硬 ...

  7. linux进阶之gitlab仓库搭建及免密使用

    一.Gitlab简介 GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务. 可通过Web界面进行访问公开的或者私人项目.它拥有与Github类 ...

  8. 11.2 uptime:显示系统的运行时间及负载

    uptime命令可以输出当前系统时间.系统开机到现在的运行时间.目前有多少用户在线和系统平均负载等信息. [root@cs6 ~]# uptime   17:02:25 up 1:48, 3 user ...

  9. linux命令--使用fsck修复文件系统

    使用fsck修复文件系统错误 1.问题描述 服务器maint_samba 由于服务器maint_samba (debian操作系统)没有正常关机,在重新启动过程中/dev/sdb1出现文件系统错误,需 ...

  10. ssh创建与添加密钥开启免密登陆 免确认机器指纹参数

     主要是两个步骤 1.控制主机创建密钥对(私钥和公钥) 2.把密钥对的公钥加入对方的认证列表中 [root@vps ~]# ssh-keygen [root@vps ~]# ssh-copy-id u ...