ADO.NET Entity Framework中的事务问题与MSDTC相关

6
在我们当前的项目中,我们使用ADO.NET实体框架作为应用程序的数据层。有一些任务需要在事务中运行,因为数据库中要做很多工作。我正在使用TransactionScope来包围这些任务。
using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
    // Do something...
    transactionScope.Complete();
}

问题是,一旦我使用TransactionScope,就会出现异常:

System.Data.EntityException:底层提供程序在打开时失败。 ---> System.Transactions.TransactionManagerCommunicationException:与底层事务管理器的通信失败。 ---> System.Runtime.InteropServices.COMException(0x80004005):从调用COM组件返回了错误HRESULT E_FAIL。

看起来这个错误与MSDTC(Microsoft分布式事务协调器)有关。当我更改MSDTC的安全配置时,会抛出另一个异常:

System.Data.EntityException:底层提供程序在打开时失败。 ---> System.Transactions.TransactionManagerCommunicationException:已禁用分布式事务管理器(MSDTC)的网络访问。请使用组件服务管理工具在MSDTC的安全配置中启用DTC以进行网络访问。

无论如何配置MSDTC,TransactionScope都会导致错误。有人知道这里出了什么问题吗?
8个回答

7

默认情况下,MSDTC的网络访问被禁用。要使其正常工作,您应该前往:

控制面板->管理工具->组件服务->计算机->我的电脑->右键单击->属性->MSDTC->安全配置

并勾选以下复选框:网络DTC访问、允许入站、允许出站。身份验证应根据您的环境选择。您可能还希望查看DTCPing工具以调试分布式事务。为了给您提供一个快捷方式 - 您可能需要修改注册表:

HKLM\Software\Policies\Microsoft\Windows NT\RPCRestrictRemoteClients=0 HKLM\Software\Policies\Microsoft\Windows NT\RPCEnableAuthEpResolution=1

以使所有内容正常运行。


谢谢,DTCPing工具告诉我有一些RPC端点错误。现在我正在查看http://support.microsoft.com/kb/306843和http://support.microsoft.com/kb/839880/EN-US/,也许那就是解决方案。 - Alexander
对于Windows 8,"安全配置"可以在这里找到: https://dev59.com/3nVD5IYBdhLWcg3wWaNh#27263904 - Steve's a D

3

是的,它使用Supress可以工作,因为你告诉它抑制或忽略环境事务并创建一个新的本地事务。由于这个事务是本地的,所以它不是分布式事务,因此它不使用MSDTC,但你可能不应该使用Suppress,而应该使用Required。


0

如果您想运行一些可能会失败的代码,但不想因此失败而中止事务,那么抑制事务是有用的。

你需要问自己的问题是: 你是否在transactionScope中访问多个持久性资源?我的意思是,你是否打开了连接到多个数据库?

这是一个重要的问题,因为如果您访问多个持久性资源,该事务将升级到DTC。

至少有两个支持单阶段通知的持久性资源在事务中注册。例如,仅向一个连接注册不会导致事务被提升。然而,每当您打开第二个连接到数据库并引起数据库注册时,System.Transactions基础结构检测到它是事务中的第二个持久性资源,并将其升级为MSDTC事务。 来源:MSDN

如果是这种情况,您可以通过正确嵌套您的transactionscopes来解决问题,例如:

//Create rootScope
using(TransactionScope rootScope = new TransactionScope()) 
{ 
    using(TransactionScope scope2 = new 
    TransactionScope(TransactionScopeOption.Required)) 
    {
         //Do work on DB1
         ...

         //Complete this ambient transaction
         scope2.Complete();
    } 

    using(TransactionScope scope3 = new 
    TransactionScope(TransactionScopeOption.Required)) 
    {
         //Do work on DB2
         ...

         //Complete this ambient transaction
         scope3.Complete();
    } 

    //Complete rootScope
    //The whole transaction will only be committed if you call 
    //Complete on the rootScope
    rootScope.Complete();

}

您可以在MSDN上找到关于TransactionScopes,嵌套工作原理等方面的更多信息。

希望这个答案能够帮助未来的人们。


0

这是我们在解决类似问题时使用的文章:

解决 MSDTC 问题

这基本上是 Nikolay R 答案的补充。他已经涵盖了文章中列出的一些建议。

注意:该文章是 Biztalk 文档的一部分,但适用于任何使用 MSDTC 的内容。


0

这意味着当您进入代码块时,它会抑制当前可能正在进行的任何事务,因此如果外部的“环境”事务决定回滚,则您的代码所做的任何更新都不会回滚。


0

如果你正在使用Entity Framework进行事务处理,Entity Framework会自动打开和关闭每个数据库调用的连接。因此,在使用事务时,你尝试在多个连接上分布事务。这将升级到MSDTC。

你可以在事务中将数据库上下文传递给被调用的类或函数。

也许这是你的答案: MSSQL错误'The underlying provider failed on Open'


0

如果分布式事务协调器服务未启动,则实体框架无法连接到数据库。请打开并启动分布式事务协调器。

服务 -> 分布式事务协调器


-3

嗯,当我把TransactionScopeOption更改为“Suppress”时,似乎它可以工作:

using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Suppress))
{
    ...
}

大家都知道为什么吗?


4
我认为在这种情况下它根本不使用交易,但我不确定。 - Nikolay R
是的,这个选项表示它不应该参与交易。 - Binoj Antony
1
我也遇到了同样的错误,但这绝不是最佳答案。 - Rory MacLeod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接