工具:visualStudio2022
环境:windows2010 .net6
需求:在web项目中,当向数据库中插入数据时,如果出现错误则自动回滚。总之,要么全部成功,要么全部失败。
1、新建一个类继承IAsyncActionFilter来实现核心功能
public class TransactionScopeFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
bool hasNotTransactionalAttribute = false;
/*
* 这段代码是用于检查当前上下文中的ActionDescriptor是否为ControllerActionDescriptor类型。
* 如果是,则将其转换为ControllerActionDescriptor类型并存储在变量actionDesc中。
* 这通常用于在自定义中间件或全局过滤器中访问控制器操作的详细信息。
*/
if (context.ActionDescriptor is ControllerActionDescriptor)
{
var actionDesc = (ControllerActionDescriptor)context.ActionDescriptor;
hasNotTransactionalAttribute = actionDesc.MethodInfo.IsDefined(typeof(NotTransactionalAttribute));
}
// 如果操作方法 上标注了hasNotTransactionalAttribute,执行完上下文后就终止。
if (hasNotTransactionalAttribute)
{
await next();
return;
}
// 没标注 就启用事务
using var tScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
// 等待上下文执行
var result = await next();
// 如果没有报错
if (result.Exception == null)
{
// 提交事务
tScope.Complete();
}
}
}
代码中已做了详尽的注释,这里着重说一下:
using var tScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
using这里必不可少,using
语句用于确保在代码块结束时,TransactionScope
对象能够被正确地释放和销毁,以防止资源泄露。
2、创建一个自定义属性类
因为有些地方我们可能不需要启用事务,在controller中的方法中我们可以在方法方面使用这个属性,就可以关闭自动启用事务功能。
/// <summary>
/// 这是一个自定义的ASP.NET Core MVC属性类,
/// 用于标记方法不应在事务中执行。
/// 它使用AttributeUsage特性指定该属性只能应用于方法上,
/// 并将AttributeTargets设置为Method以确保只适用于方法级别。
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class NotTransactionalAttribute:Attribute
{
}
3、在program.cs中注册这个筛选器
// 这段代码是用于配置ASP.NET Core MVC应用程序中的全局过滤器。
// 它将一个名为TransactionScopeFilter的自定义过滤器添加到MVCOptions中,
// 以便在所有控制器和视图中使用该过滤器。
builder.Services.Configure<MvcOptions>(options =>
{
options.Filters.Add<TransactionScopeFilter>();
});
4、在路由控制器中使用
[ApiController]
[Route("/user")]
public class UserController : Controller
{
private MyDebContext _debContext;
public UserController(MyDebContext debContext)
{
_debContext = debContext;
}
[HttpPost]
public async Task<string> Save()
{
var u = new User { Name = "回滚" };
_debContext.Users.Add(u);
await _debContext.SaveChangesAsync();
// 主动抛出一个错误
throw new Exception();
var y = new User { Name = "啧啧啧" };
_debContext.Users.Add(y);
// 写入数据库
await _debContext.SaveChangesAsync();
return "用户存储成功!";
}
}
这里使用throw new Exception()主动抛出一个错误来进行测试,测试结果就是,new User { Name = "回滚" }不会被写入数据库,大家可以更具业务场景来编写。