- A+
所属分类:.NET技术
.NetCore基于SqlSugar和Aop的工作单元模式(UnitOfWork)实现
Unit Of Work 是什么
Unit Of Work模式,即工作单元,它是一种数据访问模式。它是用来维护一个由已经被业务修改(如增加、删除和更新等)的业务对象组成的列表。它负责协调这些业务对象的持久化工作及并发问题。通过数据库事务Unit Of Work模式会记录所有对象模型修改过的信息,在提交的时候,一次性修改,并把结果同步到数据库。 这个过程通常被封装在事务中。所以在采用Unit Of Work模式好处就在于能够确保数据的完整性,如果在持有一系列业务对象(同属于一个事务)的过程中出现问题,就可以将所有的修改回滚,以确保数据始终处于有效状态,不会出现脏数据。
用一句话概括就是:它让一个接口内的所有增删改操作都在一个事务中,要么同时成功提交,要么同时失败回滚。
代码实现
创建工作单元依赖接口:
/// <summary> /// 工作单元依赖接口 /// </summary> public interface IUnitOfWork { /// <summary> /// 开启工作单元处理 /// </summary> /// <param name="context"></param> /// <param name="unitOfWork"></param> void BeginTransaction(ActionExecutingContext context); /// <summary> /// 提交工作单元处理 /// </summary> /// <param name="resultContext"></param> /// <param name="unitOfWork"></param> void CommitTransaction(ActionExecutedContext resultContext); /// <summary> /// 回滚工作单元处理 /// </summary> /// <param name="resultContext"></param> /// <param name="unitOfWork"></param> void RollbackTransaction(ActionExecutedContext resultContext); /// <summary> /// 执行完毕(无论成功失败) /// </summary> /// <param name="context"></param> /// <param name="resultContext"></param> void OnCompleted(ActionExecutingContext context, ActionExecutedContext resultContext); }
继承接口并实现:
public class SqlSugarUnitOfWork : IUnitOfWork { private readonly ISqlSugarClient _sqlSugarClient; public SqlSugarUnitOfWork(ISqlSugarClient sqlSugarClient) { this._sqlSugarClient = sqlSugarClient; } public void BeginTransaction(ActionExecutingContext context) { _sqlSugarClient.AsTenant().BeginTran(); } public void CommitTransaction(ActionExecutedContext resultContext) { _sqlSugarClient.AsTenant().CommitTran(); } public void OnCompleted(ActionExecutingContext context, ActionExecutedContext resultContext) { _sqlSugarClient.Dispose(); } public void RollbackTransaction(ActionExecutedContext resultContext) { _sqlSugarClient.AsTenant().RollbackTran(); } }
注入到容器中:
services.AddTransient<IUnitOfWork, SqlSugarUnitOfWork>(); // 注册工作单元到容器
此时,工作单元已经添加到容器中,为了更方便的使用,我们可以使用AspNetCore框架自带的过滤器实现AOP的方式来使用工作单元。
首先需要创建工作单元特性(特性是为了方便配置哪些接口需要启用工作单元):
/// <summary> /// 工作单元配置特性 /// </summary> [AttributeUsage(AttributeTargets.Method)] public class UnitOfWorkAttribute : Attribute {}
然后创建过滤器:
/// <summary> /// 工作单元Action过滤器 /// </summary> public class UnitOfWorkFilter : IAsyncActionFilter, IOrderedFilter { private readonly ILogger<UnitOfWorkFilter> _logger; public UnitOfWorkFilter(ILogger<UnitOfWorkFilter> logger) { this._logger = logger; } /// <summary> /// 过滤器排序 /// </summary> internal const int FilterOrder = 999; /// <summary> /// 排序属性 /// </summary> public int Order => FilterOrder; /// <summary> /// 拦截请求 /// </summary> /// <param name="context">动作方法上下文</param> /// <param name="next">中间件委托</param> public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 获取动作方法描述器 var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; var method = actionDescriptor.MethodInfo; // 获取请求上下文 var httpContext = context.HttpContext; // 如果没有定义工作单元过滤器,则跳过 if (!method.IsDefined(typeof(UnitOfWorkAttribute), true)) { // 调用方法 _ = await next(); return; } // 打印工作单元开始消息 _logger.LogInformation($@"{nameof(UnitOfWorkFilter)} Beginning"); // 解析工作单元服务 var _unitOfWork = httpContext.RequestServices.GetRequiredService<IUnitOfWork>(); // 调用开启事务方法 _unitOfWork.BeginTransaction(context); // 获取执行 Action 结果 var resultContext = await next(); if (resultContext == null || resultContext.Exception == null) { // 调用提交事务方法 _unitOfWork.CommitTransaction(resultContext); } else { // 调用回滚事务方法 _unitOfWork.RollbackTransaction(resultContext); } // 调用执行完毕方法 _unitOfWork.OnCompleted(context, resultContext); // 打印工作单元结束消息 _logger.LogInformation($@"{nameof(UnitOfWorkFilter)} Ending"); } }
需要将过滤器添加到通信管道中才会起作用:
services.AddControllers(options => { // 添加 工作单元过滤器 options.Filters.Add<UnitOfWorkFilter>(); });
到这一步,工作单元已经准备好了。
接下来是调用方式,很简单,只要在你想启用工作单元的接口上(也就是Controller中的Action),加上工作单元的特性就可以了,像这样:
[Route("test")] public class TestController : ControllerBase { private readonly IBaseRepository<Student> _studentRepository; private readonly IBaseRepository<Teacher> _teacherRepository; public TestController(IBaseRepository<Student> studentRepository, IBaseRepository<Teacher> teacherRepository) { this._studentRepository = studentRepository; this._teacherRepository = teacherRepository; } [HttpGet, UnitOfWork] public async Task<IActionResult> AddTestAsync() { await _studentRepository.InsertAsync(new Student { Name = "Hello", Age = 22 }); await _teacherRepository.InsertAsync(new Teacher { Name = "World", Age = 35, Subject = 1 }); return Ok("Ok"); } }
由上面的例子可以看到,_studentRepository 和 _teacherRepository 两个仓储分别进行了添加操作,但是他们实际是在一个事务中进行的,所以如果出现异常,事务中的所有操作都会回滚,从而达到操作原子性。
为了缩减篇幅,代码我省略一部分,详细的示例项目上传到GitHub:
https://github.com/young-qiang/UnitOfWork.Demo.git