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


总结

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

相关推荐
拾贰_C5 分钟前
【SpringBoot】MyBatisPlus(MP | 分页查询操作
java·spring boot·后端·spring·maven·apache·intellij-idea
就叫飞六吧5 小时前
Spring Security 集成指南:避免 CORS 跨域问题
java·后端·spring
冼紫菜6 小时前
[特殊字符]CentOS 7.6 安装 JDK 11(适配国内服务器环境)
java·linux·服务器·后端·centos
秋野酱8 小时前
Spring Boot 项目的计算机专业论文参考文献
java·spring boot·后端
香饽饽~、8 小时前
【第二篇】 初步解析Spring Boot
java·spring boot·后端
你是狒狒吗8 小时前
消息队列了解一哈
后端
Chandler249 小时前
Go语言 GORM框架 使用指南
开发语言·后端·golang·orm
蚂蚁在飞-10 小时前
Golang基础知识—cond
开发语言·后端·golang
江沉晚呤时12 小时前
.NET Core 中 Swagger 配置详解:常用配置与实战技巧
前端·.netcore