ToB企服应用市场:ToB评测及商务社交产业平台

标题: .NetCore基于SqlSugar的工作单元—UnitOfWork [打印本页]

作者: 八卦阵    时间: 2022-9-16 17:16
标题: .NetCore基于SqlSugar的工作单元—UnitOfWork
.NetCore基于SqlSugar和Aop的工作单元模式(UnitOfWork)实现

Unit Of Work 是什么

Unit Of Work模式,即工作单元,它是一种数据访问模式。它是用来维护一个由已经被业务修改(如增加、删除和更新等)的业务对象组成的列表。它负责协调这些业务对象的持久化工作及并发问题。通过数据库事务Unit Of Work模式会记录所有对象模型修改过的信息,在提交的时候,一次性修改,并把结果同步到数据库。 这个过程通常被封装在事务中。所以在采用Unit Of Work模式好处就在于能够确保数据的完整性,如果在持有一系列业务对象(同属于一个事务)的过程中出现问题,就可以将所有的修改回滚,以确保数据始终处于有效状态,不会出现脏数据。
用一句话概括就是:它让一个接口内的所有增删改操作都在一个事务中,要么同时成功提交,要么同时失败回滚。
代码实现

创建工作单元依赖接口:
  1. /// <summary>
  2. /// 工作单元依赖接口
  3. /// </summary>
  4. public interface IUnitOfWork
  5. {
  6.     /// <summary>
  7.     /// 开启工作单元处理
  8.     /// </summary>
  9.     /// <param name="context"></param>
  10.     /// <param name="unitOfWork"></param>
  11.     void BeginTransaction(ActionExecutingContext context);
  12.     /// <summary>
  13.     /// 提交工作单元处理
  14.     /// </summary>
  15.     /// <param name="resultContext"></param>
  16.     /// <param name="unitOfWork"></param>
  17.     void CommitTransaction(ActionExecutedContext resultContext);
  18.     /// <summary>
  19.     /// 回滚工作单元处理
  20.     /// </summary>
  21.     /// <param name="resultContext"></param>
  22.     /// <param name="unitOfWork"></param>
  23.     void RollbackTransaction(ActionExecutedContext resultContext);
  24.     /// <summary>
  25.     /// 执行完毕(无论成功失败)
  26.     /// </summary>
  27.     /// <param name="context"></param>
  28.     /// <param name="resultContext"></param>
  29.     void OnCompleted(ActionExecutingContext context, ActionExecutedContext resultContext);
  30. }
复制代码
继承接口并实现:
  1. public class SqlSugarUnitOfWork : IUnitOfWork
  2. {
  3.     private readonly ISqlSugarClient _sqlSugarClient;
  4.     public SqlSugarUnitOfWork(ISqlSugarClient sqlSugarClient)
  5.     {
  6.         this._sqlSugarClient = sqlSugarClient;
  7.     }
  8.     public void BeginTransaction(ActionExecutingContext context)
  9.     {
  10.         _sqlSugarClient.AsTenant().BeginTran();
  11.     }
  12.     public void CommitTransaction(ActionExecutedContext resultContext)
  13.     {
  14.         _sqlSugarClient.AsTenant().CommitTran();
  15.     }
  16.     public void OnCompleted(ActionExecutingContext context, ActionExecutedContext resultContext)
  17.     {
  18.         _sqlSugarClient.Dispose();
  19.     }
  20.     public void RollbackTransaction(ActionExecutedContext resultContext)
  21.     {
  22.         _sqlSugarClient.AsTenant().RollbackTran();
  23.     }
  24. }
复制代码
注入到容器中:
  1. services.AddTransient<IUnitOfWork, SqlSugarUnitOfWork>(); // 注册工作单元到容器
复制代码
此时,工作单元已经添加到容器中,为了更方便的使用,我们可以使用AspNetCore框架自带的过滤器实现AOP的方式来使用工作单元。
首先需要创建工作单元特性(特性是为了方便配置哪些接口需要启用工作单元):
  1. /// <summary>
  2. /// 工作单元配置特性
  3. /// </summary>
  4. [AttributeUsage(AttributeTargets.Method)]
  5. public class UnitOfWorkAttribute : Attribute {}
复制代码
然后创建过滤器:
  1. /// <summary>
  2. /// 工作单元Action过滤器
  3. /// </summary>
  4. public class UnitOfWorkFilter : IAsyncActionFilter, IOrderedFilter
  5. {
  6.     private readonly ILogger<UnitOfWorkFilter> _logger;
  7.     public UnitOfWorkFilter(ILogger<UnitOfWorkFilter> logger)
  8.     {
  9.         this._logger = logger;
  10.     }
  11.     /// <summary>
  12.     /// 过滤器排序
  13.     /// </summary>
  14.     internal const int FilterOrder = 999;
  15.     /// <summary>
  16.     /// 排序属性
  17.     /// </summary>
  18.     public int Order => FilterOrder;
  19.     /// <summary>
  20.     /// 拦截请求
  21.     /// </summary>
  22.     /// <param name="context">动作方法上下文</param>
  23.     /// <param name="next">中间件委托</param>
  24.     public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
  25.     {
  26.         // 获取动作方法描述器
  27.         var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
  28.         var method = actionDescriptor.MethodInfo;
  29.         // 获取请求上下文
  30.         var httpContext = context.HttpContext;
  31.         // 如果没有定义工作单元过滤器,则跳过
  32.         if (!method.IsDefined(typeof(UnitOfWorkAttribute), true))
  33.         {
  34.             // 调用方法
  35.             _ = await next();
  36.             return;
  37.         }
  38.         // 打印工作单元开始消息
  39.         _logger.LogInformation($@"{nameof(UnitOfWorkFilter)} Beginning");
  40.         // 解析工作单元服务
  41.         var _unitOfWork = httpContext.RequestServices.GetRequiredService<IUnitOfWork>();
  42.         // 调用开启事务方法
  43.         _unitOfWork.BeginTransaction(context);
  44.         // 获取执行 Action 结果
  45.         var resultContext = await next();
  46.         if (resultContext == null || resultContext.Exception == null)
  47.         {
  48.             // 调用提交事务方法
  49.             _unitOfWork.CommitTransaction(resultContext);
  50.         }
  51.         else
  52.         {
  53.             // 调用回滚事务方法
  54.             _unitOfWork.RollbackTransaction(resultContext);
  55.         }
  56.         // 调用执行完毕方法
  57.         _unitOfWork.OnCompleted(context, resultContext);
  58.         // 打印工作单元结束消息  
  59.         _logger.LogInformation($@"{nameof(UnitOfWorkFilter)} Ending");
  60.     }
  61. }
复制代码
需要将过滤器添加到通信管道中才会起作用:
  1. services.AddControllers(options =>
  2. {
  3.     // 添加 工作单元过滤器
  4.     options.Filters.Add<UnitOfWorkFilter>();
  5. });
复制代码
到这一步,工作单元已经准备好了。
接下来是调用方式,很简单,只要在你想启用工作单元的接口上(也就是Controller中的Action),加上工作单元的特性就可以了,像这样:
  1. [Route("test")]
  2. public class TestController : ControllerBase
  3. {
  4.     private readonly IBaseRepository<Student> _studentRepository;
  5.     private readonly IBaseRepository<Teacher> _teacherRepository;
  6.     public TestController(IBaseRepository<Student> studentRepository,
  7.         IBaseRepository<Teacher> teacherRepository)
  8.     {
  9.         this._studentRepository = studentRepository;
  10.         this._teacherRepository = teacherRepository;
  11.     }
  12.     [HttpGet, UnitOfWork]
  13.     public async Task<IActionResult> AddTestAsync()
  14.     {
  15.         await _studentRepository.InsertAsync(new Student
  16.         {
  17.             Name = "Hello",
  18.             Age = 22
  19.         });
  20.         await _teacherRepository.InsertAsync(new Teacher
  21.         {
  22.             Name = "World",
  23.             Age = 35,
  24.             Subject = 1
  25.         });
  26.         return Ok("Ok");
  27.     }
  28. }
复制代码
由上面的例子可以看到,_studentRepository 和 _teacherRepository 两个仓储分别进行了添加操作,但是他们实际是在一个事务中进行的,所以如果出现异常,事务中的所有操作都会回滚,从而达到操作原子性。
为了缩减篇幅,代码我省略一部分,详细的示例项目上传到GitHub:
  1. https://github.com/young-qiang/UnitOfWork.Demo.git
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4