idbcontexttransaction 的 rollback() 不抛异常且不中断执行,仅撤销未提交变更;必须显式 throw 异常以通知业务失败,否则上层误判成功。

直接说结论:IDbContextTransaction 的 Rollback() 不会自动抛异常,也不会阻止后续代码执行——它只撤销已发往数据库但未提交的变更;如果没调用 Commit() 且连接关闭,事务通常会静默回滚,但这不可靠,必须显式调用 Rollback() 或 Dispose()(取决于上下文)。
为什么 Rollback() 后还要手动 throw 异常?
事务回滚本身不等于业务失败处理。调用 transaction.Rollback() 只是告诉数据库“把这次事务里的改动全撤了”,但 C# 线程仍继续往下跑。若不主动 throw 或返回错误状态,上层可能误以为操作成功。
-
Rollback()是纯数据库行为,不改变托管线程控制流 - 常见错误:在
catch块里只写transaction.Rollback(),忘了throw或return - 后果:日志显示“已回滚”,但调用方收到
true或空响应,逻辑继续执行,可能引发二次错误
IDbContextTransaction 和 SqlTransaction 的关键区别
前者是 EF Core 抽象层事务,后者是 ADO.NET 底层事务。二者生命周期和释放语义不同:
-
IDbContextTransaction必须配合DbContext使用;BeginTransaction()返回的对象在Dispose()时若未Commit(),默认触发回滚(EF Core 5+ 行为) -
SqlTransaction更底层,Rollback()必须显式调用,且不能在连接关闭后调用(否则抛InvalidOperationException) - 混合使用风险高:比如在
DbContext.Database.CurrentTransaction外另开SqlConnection.BeginTransaction(),会导致事务上下文错乱
容易被忽略的连接与作用域陷阱
事务不是“开了就一直有效”。它的有效性严格绑定于底层连接状态和 DbContext 实例生命周期:
- 如果
DbContext被Dispose()(例如using块结束),而事务既没Commit()也没Rollback(),EF Core 会在Dispose()时尝试回滚——但前提是连接仍处于打开状态 - 连接意外中断(如网络抖动、SQL Server 主动踢出空闲连接)会导致
Rollback()抛InvalidOperationException:“The transaction has been aborted.” - 跨
async/await边界未正确传递事务上下文(比如用了Task.Run包裹事务代码),会导致DbContext拿不到当前事务
最危险的点其实是“以为回滚了,其实没生效”:比如在 try 块末尾忘了 Commit(),又没写 finally 保障 Rollback(),还依赖 Dispose() 自动兜底——这在连接已断或上下文提前释放时完全失效。











