ASP.NET Core自动事务ActionFilter

文章目录


前言

一、自动事务的ActionFilter

ActionFilter是用于在控制器动作执行前后插入逻辑的,要实现自动事务,应该是在OnActionExecuting开始事务,在OnActionExecuted根据结果提交或回滚。

二、使用步骤

1)创建事务控制标注特性

  1. 定义一个 [Transactional ] 特性,用于标记需要启用事务的 ControllerAction

    bash 复制代码
    using System.Data;
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
    public class TransactionalAttribute : Attribute
    {
        // 可选:扩展属性,例如隔离级别
        public IsolationLevel IsolationLevel { get; set; } = IsolationLevel.ReadCommitted;
    }

2)实现事务过滤器

  1. 创建一个支持事务自动管理的 Action Filter ,并根据 [Transactional ] 标注决定是否启用事务

    bash 复制代码
    using System.Data;
    using Microsoft.AspNetCore.Mvc.Controllers;
    using Microsoft.AspNetCore.Mvc.Filters;
    using Microsoft.EntityFrameworkCore;
    using InfrastructureLibrary.Data;
    
    public class TransactionFilter : IAsyncActionFilter
    {
        private readonly MyDbContext _dbContext;
    
        public TransactionFilter(MyDbContext dbContext)
        {
            _dbContext = dbContext;
        }
    
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var (isTransactional, isolationLevel) = CheckTransactionAttribute(context);
            if (!isTransactional)
            {
                await next();
                return;
            }
            await using var transaction= await _dbContext.Database.BeginTransactionAsync(isolationLevel);
            try
            {
                var res = await next();
                if (res.Exception != null)
                {
                    await transaction.RollbackAsync();
                }
                else
                {
                    await transaction.CommitAsync();
                }
            }
            catch (Exception)
            {
    
                await transaction.RollbackAsync();
                throw;
            }
        }
    
        private (bool IsTransactional, IsolationLevel IsolationLevel)
            CheckTransactionAttribute(ActionExecutingContext context) 
        {
            var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
            if (actionDescriptor == null) return (false, default);
    
            var attribute = actionDescriptor.MethodInfo
                .GetCustomAttributes(typeof(TransactionalAttribute), true)
                .Cast<TransactionalAttribute>()
                .FirstOrDefault()
                ??actionDescriptor.ControllerTypeInfo.GetCustomAttributes(typeof(TransactionalAttribute),true)
                .Cast<TransactionalAttribute>()
                .FirstOrDefault();
    
            return (attribute != null, attribute?.IsolationLevel ?? IsolationLevel.ReadCommitted);
    
    
        }
    }

3)注册过滤器和 DbContext

  1. 注册过滤器

    bash 复制代码
    builder.Services.AddControllers(opt =>
    {
        opt.Filters.Add<TransactionFilter>();
    });
  2. 注册DbContext

    bash 复制代码
    builder.Services.AddDbContext<MyDbContext>(opt => {
        string connStr = builder.Configuration.GetConnectionString("DefaultConnection");
        opt.UseSqlServer(connStr);
    });

4)使用标注控制事务

  1. 在需要事务的 ControllerAction 上添加 [Transactional ] 特性。

    bash 复制代码
     [Route("api/[controller]/[action]")]
     [ApiController]
     [Transactional]
     public class TestFilterController : ControllerBase
     {
         private readonly IBookRepository _bookRepository;
         private readonly IPersonRepository _personRepository;
    
         public TestFilterController(IBookRepository bookRepository, IPersonRepository personRepository)
         {
             _bookRepository = bookRepository;
             _personRepository = personRepository;
         }
    
         [HttpPost]
         //[Transactional]
         [Transactional(IsolationLevel =IsolationLevel.Serializable)]//方法级别自定义隔离级别
         public async Task<IActionResult> TestAdd()
         {
             await _bookRepository.AddAsync(new Book { Author="33", Title="44",Price=30});
             await _personRepository.AddAsync(new Person { Name = "gggggggggggg", Age = 20 });
             return Ok();
         }
         [HttpGet]
         public ActionResult<string> Test()
         {
             Console.WriteLine("Action执行中");
             var res = System.IO.File.ReadAllText("d:/asdf.txt");
             return res;
         }
     }

三、关键机制说明

  1. 标注驱动

    通过 [Transactional] 特性标记需要事务的方法或控制器,过滤器自动检测并开启事务。

  2. 作用域与生命周期
    TransactionFilter 注册为 Scoped ,确保每个请求有独立的事务。
    DbContext 默认注册为 Scoped,与事务生命周期一致。

  3. 隔离级别配置

    可通过 [Transactional(IsolationLevel = ...)] 指定事务隔离级别。

    • 查询操作(低隔离 ):可能使用 ReadCommitted(默认),允许较高的并发性。

    • 资金转账(高隔离 ):可能使用 Serializable,严格避免脏读、不可重复读、幻读。

    • 批量导入(自定义 ):可能选择 RepeatableRead,平衡性能和一致性。

    • 常见隔离级别及其行为

      隔离级别 允许的并发问题 适用场景
      ReadUncommitted 允许脏读、不可重复读、幻读 对性能要求极高,可接受数据不一致
      ReadCommitted (默认) 防止脏读,允许不可重复读、幻读 多数业务场景(如普通查询和更新)
      RepeatableRead 防止脏读、不可重复读,允许幻读 需要保证同一事务内多次读取结果一致
      Serializable 防止所有并发问题(最高隔离,性能最低) 资金操作、严格一致性要求(如库存扣减)
  4. 异常处理
    Action 执行异常时自动回滚事务。

    未标记 [Transactional] 的请求直接跳过事务逻辑。

四、扩展场景

  • 多数据库支持

    注入多个 DbContext 并在事务中协调(需要分布式事务支持)。

  • 自定义过滤逻辑

    CheckTransactionalAttribute 中添加更复杂的判断逻辑(如基于用户角色)。

  • 与现有过滤器集成

    通过 Order 属性控制过滤器的执行顺序。


总结

通过此方案,开发者可以灵活控制事务的启用和配置,同时保持代码简洁性和数据一致性。

相关推荐
柏油4 小时前
MySQL InnoDB 行锁
数据库·后端·mysql
咖啡调调。4 小时前
使用Django框架表单
后端·python·django
白泽talk4 小时前
2个小时1w字| React & Golang 全栈微服务实战
前端·后端·微服务
摆烂工程师4 小时前
全网最详细的5分钟快速申请一个国际 “edu教育邮箱” 的保姆级教程!
前端·后端·程序员
一只叫煤球的猫4 小时前
你真的会用 return 吗?—— 11个值得借鉴的 return 写法
java·后端·代码规范
Asthenia04125 小时前
HTTP调用超时与重试问题分析
后端
颇有几分姿色5 小时前
Spring Boot 读取配置文件的几种方式
java·spring boot·后端
AntBlack5 小时前
别说了别说了 ,Trae 已经在不停优化迭代了
前端·人工智能·后端
@淡 定5 小时前
Spring Boot 的配置加载顺序
java·spring boot·后端