.NET TransactionScope 和 MSDTC

.NET TransactionScope and MSDTC

在我的代码中有要处理的事务:

using (var scope = new TransactionScope())
{
  repo1.SaveSomething();
  repo2.SaveAnythingElse();
  scope.Complete();
}

在 repo1 和 repo2 函数内部通过使用创建它们自己的数据库上下文,并处理它们,事务非常有效。

现在我像这样添加另一个代码,它开始抛出一个异常:

The underlying provider failed on Open. (EntityFramework) Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool. (System.Transactions) The transaction manager has disabled its support for remote/network transactions.

我读到,尽管使用相同 sql 服务器相同的数据库,但在事务内部打开连接时 - 它需要 MSDTC 组件来处理它。我将代码更改为以下内容:

using (var scope = new TransactionScope(TransactionScopeOption.Required, 
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted }))
{
   ....
   scope.Complete();
}

现在异常消失了。

我的问题:

我认为是简单的问题 :) 如有任何帮助,我们将不胜感激!

1) 您绝对应该为您的 TransactionScope 使用 ReadCommitted 而不是默认的 Serializable,但这与您的问题无关,请参阅 here

2) 当您有一个活动的 TransactionScope 时,只要您打开一个 SqlConnection,它就会被加入到该事务中。如果没有其他资源参与事务,SqlClient 将开始本地或 "lightweight" 事务。这涉及MSTDC;这只是在打开的 SqlConnection 上启动的正常 SQL 服务器事务。

如果您关闭该 SqlConnection(或处置包含它的 EF DbContext),该连接将返回到连接池。但它与其他池连接隔离开来,只是挂起,直到事务完成或回滚。

如果您在同一个 TransactionScope 中打开一个新的 SqlConnection,完全 相同的 ConnectionString,连接池不会获取新连接,只会返回现有连接已经加入交易。

如果您在同一个 TransactionScope 中使用不同的 ConnectionString 打开一个新的 SqlConnection,或者当连接池中没有连接已经登记在 Transaction 中时,您将获得一个新的 SqlConnection 并将登记在交易。但是由于事务中已经有另一个 SqlConnection,这将需要 MSTDC 创建一个真正的分布式事务。这叫做"promotion";你的 "lightweight transaction" 是 "promoted" 到 "distributed transaction".

因此,在该背景下,审核您的连接生命周期和 ConnectionString 使用情况,以了解您为何在此处触发升级。

换句话说,通过正确使用 ConnectionString 和连接生命周期管理,您应该能够 运行 此代码:

using (var scope = new TransactionScope())
{
  repo1.SaveSomething();
  repo2.SaveAnythingElse();
  scope.Complete();
}

不触发分布式事务。