transactionscope未提交的根本原因是事务上下文未捕获数据库操作,常见于连接非块内创建、连接池关闭、使用不支持的提供程序、与ef core手动事务冲突等场景。

事务不是“写了就生效”,而是“没写对就静默失败”——尤其用 TransactionScope 时,不调用 Complete() 就等于没提交,哪怕代码跑完、没报错、日志也空空如也。
为什么 TransactionScope 调用了 Complete() 却没提交?
这不是代码逻辑错了,而是事务上下文根本没捕获到你的数据库操作。常见原因有:
-
SqlConnection实例必须在using (var scope = new TransactionScope())块内首次创建和打开;复用块外已打开的连接,事务不生效 - 连接字符串里含
Pooling=false,连接池关闭 → 事务上下文丢失 - 用了
OleDbConnection或其他不支持System.Transactions传播的提供程序,TransactionScope对它完全透明 - EF Core 中手动调用了
Database.BeginTransaction(),和当前TransactionScope冲突,导致事务被“抢走”
SqlTransaction 和 TransactionScope 该怎么选?
两者不是替代关系,而是适用场景不同:
- 单库、单连接、控制粒度细(比如只包几条
SqlCommand)→ 用SqlTransaction:轻量、明确、无隐式行为 - 跨多个 DbContext / 多个数据库连接 / 涉及非数据库资源(如消息队列、文件写入)→ 必须用
TransactionScope:靠事务传播自动登记资源 - 想避免 MSDTC 激活?确保只开一个
SqlConnection、连同一个 SQL Server 实例、不用RequiresNew嵌套 → 默认走 LTM(本地事务管理器),不惊动分布式事务服务
嵌套 TransactionScope 时最容易漏掉什么?
不是“外层 Complete() 就够了”,而是每个 scope 都得显式完成:
- 所有嵌套的
TransactionScope实例都必须调用Complete(),缺一个,整个链路在Dispose()时全部回滚 - 若内层用了
TransactionScopeOption.RequiresNew,它的Complete()只影响自己事务,外层仍需独立调用 —— 二者失败互不影响,但成功也互不代劳 - 最隐蔽的坑:内层
try-catch吞了异常(比如空catch { }),导致没走到Complete(),而外层照常执行 → 数据库静默丢数据
隔离级别设置不当会引发什么真实问题?
IsolationLevel.ReadCommitted 是默认值,但它不解决所有并发问题:
- 用
ReadCommitted仍可能遇到“不可重复读”:同一事务中两次查同一条记录,中间被别的事务改过并提交,结果不一致 - 用
RepeatableRead能防不可重复读,但幻读(新插入行)依然存在;真要强一致性,得上Serializable,但锁范围扩大,容易阻塞 - EF Core 中设置隔离级别必须在
TransactionScope构造时传入,不能靠DbContext.Database.BeginTransaction(IsolationLevel.Serializable)后期覆盖
事务真正的复杂点不在语法,而在“谁参与了、谁没参与、谁悄悄退出了”。连接生命周期、资源注册时机、异常吞没位置——这些地方一松懈,Commit() 或 Complete() 就成了无效仪式。











