.NET TransactionScope和MSDTC

问题描述 投票:3回答:1

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

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();
}

现在异常消失了。

我的问题:

  • 为什么先前代码中使用的事务永​​远不会丢弃异常?
  • 为什么新代码会丢掉它?
  • 改变后为什么它不再下降?

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

c# entity-framework transactions distributed-transactions msdtc
1个回答
2
投票

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();
}

不触发分布式事务。

© www.soinside.com 2019 - 2024. All rights reserved.