using CloudBuilder.Core.Service; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Transactions; namespace CloudBuilder.Core.DatabaseAccessor { /* 事务范围:TransactionScopeOption.RequiresNew 管理的是分布式事务, TransactionScopeOption.Required但对于单个数据库连接,通常不需要分布式事务。 OnActionExecutionAsync 是 ASP.NET Core 中异步的 Action 过滤器执行方法。 // OnActionExecuting: Action 执行前 [开始事务] ↓ [Action 方法执行] ↓ // OnActionExecuted: Action 执行后(无论是否异常) [检查异常] //Controller对象不抛出异常,返回的是异常对象ValidatedException、ServiceErrorException(方便前端信息统一处理) [提交或回滚事务] 注意(并发问题修复): 1) ActionFilter 的实例是「每个 Action 一份、被所有并发请求共用」的,不是每请求一个。 原来把 TransactionScope 存在实例字段 _transactionScope 上,两个请求重叠时: A 请求刚写下自己的 scope,B 请求立刻把它覆盖成自己的 scope(在另一个线程上创建), A 请求结束时 Dispose 的其实是 B 的 scope,于是抛 "A TransactionScope must be disposed on the same thread that it was created." 而且还会把别人的事务提前提交/回滚。现在改为按请求存放在 HttpContext.Items 里。 2) 创建时必须带 TransactionScopeAsyncFlowOption.Enabled,否则事务与创建线程强绑定, Action 里 await 之后续体落到别的线程池线程,Dispose 同样会抛上面那条异常。 提交/回滚的判断逻辑(ValidatedException / ServiceErrorException → 回滚)保持原样未变。 */ [AttributeUsage(AttributeTargets.Method)] public class UnitOfWorkAttribute : ActionFilterAttribute { /// /// 事务对象在 HttpContext.Items 里的键。 /// 用 HttpContext.Items 而不是实例字段:过滤器实例被所有并发请求共用, /// 实例字段会让并发请求互相覆盖(详见类注释)。 /// private const string ScopeKey = "__UnitOfWork_TransactionScope__"; public override void OnActionExecuting(ActionExecutingContext context) { // 同一请求内重复进入(嵌套)时复用外层事务,不重复开启 if (context.HttpContext.Items.ContainsKey(ScopeKey)) { base.OnActionExecuting(context); return; } // TransactionScopeAsyncFlowOption.Enabled:允许事务随 async/线程流转, // 否则 Dispose 可能发生在另一个线程上而抛异常 var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TransactionManager.MaximumTimeout }, TransactionScopeAsyncFlowOption.Enabled); context.HttpContext.Items[ScopeKey] = scope; base.OnActionExecuting(context); } public override void OnActionExecuted(ActionExecutedContext context) { // 取不到说明本请求没有开启(或已处理),直接放行 if (context.HttpContext.Items[ScopeKey] is not TransactionScope transactionScope) { base.OnActionExecuted(context); return; } bool needRollback = false; try { // ============================================== // 核心逻辑:判断返回值是不是异常对象 // ============================================== if (context.Result is ObjectResult objectResult) { var value = objectResult.Value; if (value != null) { // 只要是这两个异常 → 必须回滚 if (value is ValidatedException || value is ServiceErrorException) { needRollback = true; } } } // 如果没有异常,才提交 if (!needRollback) { //必须要有 SaveChanges() 调用,否则数据只会停留在内存中,不会真正保存到数据库。 transactionScope.Complete(); } } finally { // 不 Complete() 就是自动回滚 transactionScope.Dispose(); context.HttpContext.Items.Remove(ScopeKey); } base.OnActionExecuted(context); } } }