TransactionScopeFilter

    [AttributeUsage(AttributeTargets.Method)]
    public class TransactionalSupportAttribute : Attribute
    {
    }

    public class TransactionScopeFilter : IAsyncActionFilter
    {
        async Task IAsyncActionFilter.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            bool transactionNeeded = false;

            if (context.ActionDescriptor is ControllerActionDescriptor descriptor)
            {
                transactionNeeded = descriptor.MethodInfo.GetCustomAttributes(typeof(TransactionalSupportAttribute), false).Any();
            }

            if (transactionNeeded)
            {
                using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    ActionExecutedContext executedContext = await next();

                    if (executedContext.Exception == null)
                        scope.Complete();
                }
            }
            else
            {
                await next();
            }
        }
    }

db context/entity

    public class Book
    {
        public int Id { get; init; }
        public string? Title { get; init; }

        public Book()
        {
        }

        public override string ToString()
        {
            return $"book:{Id}, {Title}";
        }
    }

    public class MyDbContext : DbContext
    {
        public DbSet<Book> Books { get; set; }

        public MyDbContext(DbContextOptions<MyDbContext> opt)
            : base(opt)
        {

        }
    }

注入asp.net core

builder.Services.Configure<MvcOptions>(opt =>
{
    opt.Filters.Add<TransactionScopeFilter>();
});

builder.Services.AddDbContext<MyDbContext>(builder =>
{
    builder.UseSqlServer("Server=.;Database=demo05;Trusted_Connection=True;MultipleActiveResultSets=True");
});

测试

    [ApiController]
    [Route("api/[controller]")]
    public class BookController : Controller
    {
        private MyDbContext dbContext { get; init; }

        public BookController(MyDbContext dbContext)
        {
            this.dbContext = dbContext;
        }

        [HttpPost("p1")]
        public async Task<string> Test1()
        {
            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                dbContext.Books.Add(new Book() { Title = "bbb1" });
                await dbContext.SaveChangesAsync();

                return "ok";
            }
        }

        [HttpPost("p2")]
        [TransactionalSupport]
        public async Task<string> Test2()
        {
            dbContext.Books.Add(new Book() { Title = "bbb1" });
            await dbContext.SaveChangesAsync();

            return "ok";
        }
    }