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 属性控制过滤器的执行顺序。


总结

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

相关推荐
IT_陈寒2 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码3 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo3 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志4 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..4 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects4 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰4 小时前
一些基础和问题解决
后端
陈随易4 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN4 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp
Imchendiana4 小时前
《狂人日记NO.9》— 前后端一把梭,我的全栈实录
前端·后端