日常生活的交流与学习

首页 新随笔 联系 管理

一、工作单元事务代码分析

在上一个课程,我们已经实现了数据库事务功能,来保证数据的完整性。

通过上一个课程的实现方式,我们在每一个 Action 开头和结尾,都需要添加对应的代码,才能实现事务功能,具体见以下截图:

这种实现方式存在以下 2 个问题:

  1. 在具体业务开发中,一个请求往往都会涉及多次数据库操作,所有 Action 都必须添加对应的事务代码;

  2. 虽然实现事务代码比较简单,但是代码是比较重复的,这样就会导致所有 Action 充斥着重复的代码。

二、工作单元事务重构

根据上面的分析,为了消除重复的代码,我们可以通过自定义 WebApi 的行为过滤器,来实现在 Action 执行前与执行后,添加事务开启与提交的代码。

2.1 添加行为过滤器

在 Electric.API 项目,创建文件夹 UOW,并新增 UnitOfWorkFilterAttribute 类。

UnitOfWorkFilterAttribute 完整代码如下:

using Electric.Repository.UOW;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Electric.API.UOW
{
    public class UnitOfWorkFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var _unitOfWork = context.HttpContext.RequestServices.GetService(typeof(IUnitOfWork)) as UnitOfWork;
            _unitOfWork.BeginTransaction();
        }

        public override void OnActionExecuted(ActionExecutedContext context)
        {
            var _unitOfWork = context.HttpContext.RequestServices.GetService(typeof(IUnitOfWork)) as UnitOfWork;
            _unitOfWork.Commit();
        }
    }
}

代码说明:

  1. 继承行为过滤器 ActionFilterAttribute;
  2. 实现 OnActionExecuting 方法:开启事务,此方法会在 action 方法运行之前调用;
  3. 实现 OnActionExecuted 方法:提交事务,此方法会在 action 方法运行之后调用。

2.2 事务行为过滤器使用方法

我们可以通过为每一个 Action 添加 [UnitOfWorkFilter],来实现事务功能。使用方式见以下截图:

通过自定义过滤器,可以大大简化我们的代码,我们只需为需要事务的 Action,添加特性注解就可以。

2.3 全局添加事务行为过滤器

通过为每一个 Action 添加 [UnitOfWorkFilter],可以简化我们的代码,但是我们还可以进一步简化。
WebAPI 在添加控制器服务方法里,我们可以添加 Action 的配置,从而自定义 Action 的行为。

在 MVCExtension 添加全局的工作单元过滤器。

对应的 Action 的特性,也都没必要添加,可以移除。

全局注册的优缺点:

优点:

  1. 开发人员无需关心事务的实现,只需专心业务代码的开发;

  2. 所有的 Action 默认添加事务,避免需要事务功能的 Action 遗漏。

缺点:

  1. 增加事务开销,很多 Action 只有查询命令,没必要使用事务;

  2. 存在大事务的问题,Action 里面的查询命令无需事务,但是事务特性是在 Action 执行前开启的,导致所有 Select 也会开启事务。

全局注入事务,可以大大简化我们的代码,但也存在一些缺点。所以,我们可以通过为 Action 添加特性,来实现事务的开关功能,为没必要事务的 Aciton,移除事务功能。

三、工作单元事务开关

3.1 添加事务开关

在 Electric.API 项目,新增 UnitOfWorkAttribute 类。

UnitOfWorkAttribute 完整代码如下:

namespace Electric.API.UOW;

public class UnitOfWorkAttribute : Attribute
{
    public bool IsTransactional { get; set; } = true;
}

代码说明:

1、UnitOfWorkAttribute 继承 Attribute;

2、属性 IsTransactional,用来配置是否开启事务,默认值为 true,默认开启。

3.2 修改事务过滤器特性

在事务过滤器特性,我们要通过获取 Action 的特性,来判断是否需要开启事务。

以下截图,代码实现功能:通过获取 Action 的 UnitOfWorkAttribute,来判断是否需要开启事务。

UnitOfWorkFilterAttribute 修改后的完整代码:

using Electric.Repository.UOW;

using Microsoft.AspNetCore.Mvc.Filters;

namespace Electric.API.UOW;


public class UnitOfWorkFilterAttribute : ActionFilterAttribute
{

    private UnitOfWorkAttribute? _unitOfWorkAttribute;

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        _unitOfWorkAttribute = context.ActionDescriptor.EndpointMetadata.FirstOrDefault(x => x.GetType() == typeof(UnitOfWorkAttribute)) as UnitOfWorkAttribute;

        if (_unitOfWorkAttribute != null && !_unitOfWorkAttribute.IsTransactional)
        {
            return;
        }

        var _unitOfWork = context.HttpContext.RequestServices.GetService(typeof(IUnitOfWork)) as UnitOfWork;
        _unitOfWork.BeginTransaction();
    }
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if (_unitOfWorkAttribute != null && !_unitOfWorkAttribute.IsTransactional)
        {
            return;
        }
        var _unitOfWork = context.HttpContext.RequestServices.GetService(typeof(IUnitOfWork)) as UnitOfWork;
        _unitOfWork.Commit();
    }
}

代码说明:

1、OnActionExecuting:action 执行之前,判断是否需要开启事务;

2、OnActionExecuted:action 执行之后,判断是否需要提交事务。

3.3 关闭事务

我们可以为所有的 Get 请求,关闭事务,使用方法如下:

关闭用户搜索事务:

关闭获取角色的权限列表事务:

可以为所有 Controller 的所有 Get 请求,关闭事务。

当然了,如果 Put、Post、Patch 的方法里,如果只是单个数据库操作的,也是可以关闭事务的,使用 SaveChanges 默认事务就可以。

对于一般的管理系统,并发性都比较低的,一般默认开启事务就可以,针对个别事务开销比较大的 Action,个性化开发就可以。

posted on 2024-04-30 16:46  lazycookie  阅读(9)  评论(0编辑  收藏  举报