一、工作单元事务代码分析
在上一个课程,我们已经实现了数据库事务功能,来保证数据的完整性。
通过上一个课程的实现方式,我们在每一个 Action 开头和结尾,都需要添加对应的代码,才能实现事务功能,具体见以下截图:
这种实现方式存在以下 2 个问题:
-
在具体业务开发中,一个请求往往都会涉及多次数据库操作,所有 Action 都必须添加对应的事务代码;
-
虽然实现事务代码比较简单,但是代码是比较重复的,这样就会导致所有 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();
}
}
}
代码说明:
- 继承行为过滤器 ActionFilterAttribute;
- 实现 OnActionExecuting 方法:开启事务,此方法会在 action 方法运行之前调用;
- 实现 OnActionExecuted 方法:提交事务,此方法会在 action 方法运行之后调用。
2.2 事务行为过滤器使用方法
我们可以通过为每一个 Action 添加 [UnitOfWorkFilter],来实现事务功能。使用方式见以下截图:
通过自定义过滤器,可以大大简化我们的代码,我们只需为需要事务的 Action,添加特性注解就可以。
2.3 全局添加事务行为过滤器
通过为每一个 Action 添加 [UnitOfWorkFilter],可以简化我们的代码,但是我们还可以进一步简化。
WebAPI 在添加控制器服务方法里,我们可以添加 Action 的配置,从而自定义 Action 的行为。
在 MVCExtension 添加全局的工作单元过滤器。
对应的 Action 的特性,也都没必要添加,可以移除。
全局注册的优缺点:
优点:
-
开发人员无需关心事务的实现,只需专心业务代码的开发;
-
所有的 Action 默认添加事务,避免需要事务功能的 Action 遗漏。
缺点:
-
增加事务开销,很多 Action 只有查询命令,没必要使用事务;
-
存在大事务的问题,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,个性化开发就可以。