如何在不使用MSDTC的情况下,在TransactionScope内运行两个Entity Framework上下文?

19

这个问题在简单的例子中无法重现,但我想知道是否有人有经验和技巧,以下是问题:

  • 使用 Entity Framework
  • 在应用程序中有许多点将数据写入到某些实体表中,例如Customer,以及将数据写入到历史表中
  • 这两个操作都使用Entity Framework,但它们使用不同的上下文
  • 这些操作需要在一个事务中:即如果其中一个写入失败,则不能写入另一个等等。
  • 我可以使用TransactionScope来封装它们,

像这样:

using (TransactionScope txScope = new TransactionScope()) {
    ...
}

但是我收到了以下错误信息:

Microsoft Distributed Transaction Coordinator (MSDTC) 对于网络事务已被禁用。

我们的数据库管理员告诉我,MSDTC 是出于选择而被禁用的,无法安装

因此,我正在尝试进行更改,尝试创建具有 MetadataWorkspace 的自己的 EntityConnection,并想法让每个上下文使用相同的 EntityConnection。 然而,这几乎是不可能的,例如,当前即使理论上两个上下文都在使用 EntityConnection,我仍然继续收到上面的错误。 很难理解为什么 Entity Framework 需要 MSDTC。

有人走过这条路吗? 有经验或代码示例可以分享吗?

3个回答

13

这个问题很简单。

如果你正在使用sql server 2008,你不应该有这个问题,因为你有可晋升事务(promotable transaction),而且由于.NET知道你正在使用相同的持久化存储(即数据库),它不会将其升级到DTC并将其提交为本地事务。请研究一下sql server 2008中的可晋升事务。

据我所知,Oracle正在开发其驱动程序以支持可晋升事务,但我不知道目前的状态,MS oracle驱动程序不支持它。http://www.oracle.com/technology/tech/windows/odpnet/col/odp.net_11.1.0.7.20_twp.pdf

如果你使用的驱动程序不支持可晋升事务,那么.NET无法使用两个连接进行本地事务。你应该改变你的架构或者说服数据库管理员安装MSDTC。


嗯,我们实际上正在使用SQL Server 2008,但它似乎并不知道我们在两个连接中使用的是同一个数据库:因此,我的当前方法是构建两个EntityConnection对象,都来自于一个SqlConnection对象,也许这些SQLConnection对象在两个上下文中是相同的,EF就不会将其升级为DTC。 - Edward Tanguay
更新:抱歉,数据库实际上仍然是SQL Server 2005,正如您所指出的那样似乎是问题所在。在阅读了这篇非常优秀的文章后,我再次检查了版本,并记录了大量数据,因为他正通过这个问题进行工作:https://dev59.com/tuo6XIcBkEYKwwoYTzEw - Edward Tanguay
最终你有两个选择:将SQL Server更改为2008或更改代码。祝好运! - Pablo Castilla

1

我在使用SQL 2008和Entity Framework时遇到了类似的问题。

我定义了两个框架(EF1和EF2),但是它们都使用相同的连接字符串连接到一个SQL 2008数据库。

当我在两个框架之间嵌套“using”时,就会出现上述MSDTC错误。例如,代码如下:

using (TransactionScope dbContext = new TransactionScope())
{
     using (EF1 context = new EF1())
     {
         // do some EF1 db call
         using (EF2 context2 = new EF2())
         {
              // do some EF2 db call
          }
      }
      dbContext.Complete();
}

虽然它被分散在多个方法中,但这就是"Usings"的基本结构。

修复方法是一次只打开一个using。没有MTDSC错误,也不需要在数据库上开启分布式事务。

using (TransactionScope dbContext = new TransactionScope())
{
     using (EF1 context = new EF1())
     {
         // do some EF1 db call

      }
     using (EF2 context2 = new EF2())
     {
              // do some EF2 db call
     }
     dbContext.Complete();
}

这段代码不起作用。它与调用每个生成不同上下文的方法没有区别。 - KingOfHypocrites
TransactionScope不再是EF 6上管理事务的推荐方式。我知道你写答案时考虑了EF1和EF2,但我想指出现在我们不应该使用事务范围。请参阅Microsoft的此链接:http://msdn.microsoft.com/en-us/data/dn456843.aspx - Francisco Goldenstein
1
@FranciscoGoldenstein 公平,但是 dbContext.Database.BeginTransaction() 只适用于您正在使用单个上下文并且它已经打开的情况。这是您链接的文章所涉及的唯一情况。对于更复杂的条件,它不起作用。 - Suncat2000

1

我认为您需要做的是强制使您的上下文共享单个数据库连接。然后,您将能够在单个事务中执行两个不同上下文的这两个操作。您可以通过将一个EntityConnection对象传递给两个上下文构造函数来实现此目的。当然,这种方法需要您将此对象传递给更新DB的方法。

最近我发表了博客,介绍了创建数据库上下文范围的方法,这将使使用多个EF上下文和事务变得更加容易。


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