为什么TransactionScope不能与Entity Framework一起使用?

27

请看以下代码。如果我初始化多个实体上下文,则只会在第二组代码上出现以下异常。如果注释掉第二组,则可以正常工作。

  

{"The underlying provider failed on Open."}

     

Inner: {"Communication with the underlying transaction manager has failed."}

     

Inner: {"Error HRESULT E_FAIL has been returned from a call to a COM component."}

请注意,这是一个示例应用程序,我知道连续创建2个上下文没有意义。然而,生产代码确实有理由在同一个TransactionScope中创建多个上下文,而这不能改变。

编辑

这是我之前尝试设置MS-DTC的问题。它似乎在服务器和客户端上都启用了。我不确定是否已正确设置。还要注意的是,我正在努力做到的一点是,TransactionScope中的现有代码正在使用ADO.NET和Linq 2 Sql...我希望它们也能使用相同的事务。(听起来有点疯狂,但如果可能的话,我需要让它工作)。

如何在C#中使用TransactionScope?

解决方案

Windows防火墙阻止了对MS-DTC的连接。

using(TransactionScope ts = new System.Transactions.TransactionScope())
        {
                using (DatabaseEntityModel o = new DatabaseEntityModel())
                {
                    var v = (from s in o.Advertiser select s).First();
                    v.AcceptableLength = 1;
                    o.SaveChanges();
                }

                //-> By commenting out this section, it works
                using (DatabaseEntityModel o = new DatabaseEntityModel())
                {
                    //Exception on this next line
                    var v = (from s1 in o.Advertiser select s1).First();                         v.AcceptableLength = 1;
                    o.SaveChanges();
                }
                //->

                ts.Complete();
        }
8个回答

19

您可以通过管理自己的EntityConnection并将其传递给ObjectContext来避免使用分布式事务。否则请查看以下内容。

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=580828&SiteID=1&mode=1 http://forums.microsoft.com/msdn/showpost.aspx?postid=113669&siteid=1&sb=0&d=1&at=7&ft=11&tf=0&pageid=1

EntityConnection conn = new EntityConnection(ConnectionString);

using (TransactionScope ts = new TransactionScope())
{
    using (DatabaseEntityModel o = new DatabaseEntityModel(conn))
    {
            var v = (from s in o.Advertiser select s).First();
            v.AcceptableLength = 1;
    }

    //-> By commenting out this section, it works
    using (DatabaseEntityModel o = new DatabaseEntityModel(conn))
    {
        //Exception on this next line
        var v = (from s1 in o.Advertiser select s1).First();
                v.AcceptableLength = 1;
    }
    //->

    ts.Complete();
}

我不仅仅使用Entity Framework,因此重用EntityConnection也不是一个简单的解决方案(请参见上面的编辑)。 - NotDan
6
避免使用DTC是一个不错的选择。这并不是说DTC有问题,只是分布式事务不是轻易选择的东西。它紧密地链接了应用程序和资源,这样做会因为设计而面临低可用性的风险。 - Pontus Gagge
你也可以调用 context.Connection.Open() 来手动管理它。你不需要手动创建 EntityConnection。 - Sander Rijken
我不明白——你展示了一个使用一个EntityConnection的例子,所以它应该可以工作,但是它失败了。那么这怎么能成为一个解决方案呢? - greenoldman

19

由于某些原因,您的MS-DTC(分布式事务协调器)未正常工作。 MS-DTC 用于协调跨多个异构资源(包括多个SQL连接)的事务结果。

请参考此链接了解更多信息。

基本上,如果确保MS-DTC正在运行并正常工作,则使用2个ADO.NET连接不会出现问题 - 无论它们是实体框架连接还是任何其他类型的连接。


4
现在这个可以工作了。Windows防火墙一直在阻止与MS-DTC的连接。 - NotDan

5

我把这段话放在这里,因为我昨天和一位同事花了3个小时来调试这个问题。所有关于这个问题的答案都说这通常是防火墙导致的,但在我们的情况下不是。希望这能让其他人避免痛苦。

我们目前正在迁移到Entity Framework。这意味着我们的代码中存在部分情况,在单个事务内既直接使用new SqlConnection(connectionString).Open()打开连接,也间接使用EF数据上下文打开连接。

这在我们的应用程序中已经运行良好一段时间了,但当我们开始回顾在生产环境中运行的代码并为其编写测试时,从测试运行程序执行的代码在同一事务中第一次尝试连接到数据库之后,就会抛出此错误。

最终发现这个错误的原因是,如果您没有向连接字符串提供Application Name=参数,Entity Framework会默认添加一个(类似于EntityFrameworkMUF)。这意味着您的连接池中有两个不同的连接:

  1. 手动打开的没有Application Name=参数的连接
  2. 自动生成的带有Application Name=EntityFrameworkMUF后缀的连接

在单个事务内不可能打开两个不同的连接。生产代码指定了应用程序名称,因此它可以正常工作;测试代码没有指定,所以出现了错误。指定Application Name=参数为我们修复了这个错误。


我的问题更加具体,一个连接字符串使用了 App=EntityFramework,而另一个使用了 Application Name = EntityFramework。最好的方法是测试一下是否是这个问题,可以将第一个上下文中的连接字符串复制并在创建第二个上下文时将其作为参数传递。 - Alexander Derck

5

将C:\Windows\msdtc.exe添加到防火墙和服务器的防火墙例外中。在这之前,我尝试了很长时间开放特定的端口号和范围,但都无济于事。


3

顺便提一句,当您使用像这样的显式事务时,应考虑将SaveChanges(false)与AcceptChanges()结合使用。

这样,如果SaveChanges(false)中出现问题,ObjectContext不会丢弃您的更改,因此您可以稍后重新应用或进行某些错误日志记录等操作。

有关更多信息,请参见此文章:http://blogs.msdn.com/alexj/archive/2009/01/11/savechanges-false.aspx

祝好

Alex


1

问题在于两个不同的DataContext实际上创建了两个不同的连接。

在这种情况下,事务必须升级为分布式事务。我假设您的问题来自于服务器和/或客户端上MS DTC(Microsoft Distributed Transaction Coordinator)的配置。 例如,如果服务器未配置为允许MSDTC的远程连接,则会遇到那种异常。

您可以参考此MS页面以解决MSDTC问题,而且谷歌上也有很多关于它的文章/论坛问题。

现在,可能是其他问题,但它确实听起来像是一个MSDTC问题。


请看我上面的编辑。我认为你是对的,我正在尝试设置MS-DTC...我不确定为什么它不起作用。 - NotDan

0

当我使用DTC从MQ队列中读取消息、处理并存储到SQL 2005 Express Edition数据库时,我遇到了类似的错误。我没有足够的时间去调查是2005还是Express Edition导致了这个问题,但是切换到2008 Standard版本后,这种特定的行为就消失了。


0

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