在我的代码中有要处理的事务:
using (var scope = new TransactionScope())
{
repo1.SaveSomething();
repo2.SaveAnythingElse();
scope.Complete();
}
在repo1和repo2函数内部创建自己的db上下文,使用并处理它们,这些事务就像一个魅力。
现在我添加这样的另一个代码,它开始删除异常:
底层提供程序在Open上失败。 (EntityFramework)已禁用分布式事务管理器(MSDTC)的网络访问。请使用组件服务管理工具在MSDTC的安全配置中启用DTC以进行网络访问。 (System.Transactions)事务管理器已禁用其对远程/网络事务的支持。
我读到,尽管同一个sql server使用相同的db,但是在事务内部打开连接时 - 它需要MSDTC组件来处理它。我将代码更改为以下内容:
using (var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted }))
{
....
scope.Complete();
}
现在异常消失了。
我的问题:
我认为简单的问题:)任何帮助将不胜感激!
1)对于TransactionScope,您绝对应该使用默认Serializable上的ReadCommitted,但这与您的问题无关,请参阅here。
2)当您有一个活动的TransactionScope时,无论何时打开SqlConnection,它都将在该Transaction中登记。如果没有其他资源参与该事务,SqlClient将开始本地或“轻量级”事务。这不涉及MSTDC;它只是在打开的SqlConnection上启动的普通SQL Server事务。
如果关闭该SqlConnection(或Dispose包含它的EF DbContext),则连接将返回到连接池。但是它与其他池化连接是隔离的,只是在事务完成或回滚之前就会挂起。
如果在同一TransactionScope中打开一个新的SqlConnection,具有完全相同的ConnectionString,而不是获取新连接,则连接池只会返回已在事务中登记的现有连接。
如果在具有不同ConnectionString的同一TransactionScope中打开新的SqlConnection,或者在事务中已经登记的连接池中没有连接,那么您将获得一个新的SqlConnection,它将在事务中登记。但是由于在事务中已经有另一个SqlConnection,这将需要MSTDC创建一个真正的分布式事务。这被称为“促销”;您的“轻量级事务”被“提升”为“分布式事务”。
因此,在此背景下,审核您的连接生命周期和ConnectionString用法,以了解您在此处触发促销的原因。
换句话说,通过正确的ConnectionString使用和连接生命周期管理,您应该能够运行以下代码:
using (var scope = new TransactionScope())
{
repo1.SaveSomething();
repo2.SaveAnythingElse();
scope.Complete();
}
不触发分布式事务。