.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

转:https://www.cnblogs.com/young-q/archive/2022/08/13/16583844.html

posted on 2023-03-31 15:51  静以修身俭以养德  阅读(164)  评论(0编辑  收藏  举报

导航