Entity Framework 强制分布式事务

6

我无法仅使用单个上下文操作单个数据库而不升级到MSDTC并在context.SaveChanges()上抛出异常的以下代码:

public void DeleteGroupDetails(int groupId)
{
    // Note there is no ambient tx
    var thisIsNull = Transaction.Current;

    using (var scope = new TransactionScope())
    {
        var thisIsNotNull = Transaction.Current;

        using (var context = new MyDbEntities())
        {
            var deleted = context.tblGroups.Where(x => x.GroupID == groupId);

            context.tblGroups.RemoveRange(deleted);

            try
            {
                context.SaveChanges();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }

        //scope.Complete();
    }
}

异常为 “底层提供程序在打开时失败。--> 伙伴事务管理器已禁用其对远程/网络事务的支持。(HRESULT 异常:0x8004D025)”。
注意:
- 此处使用 EF 6 和 SQL Server 2005。 - 由于数据库服务器被锁定,无法使用 MSDTC。 - 我想在 POC 中使用 TransactionScope,因为这将在 WCF 中运行,而 WCF 内置了 TransactionScope,我不想在代码中添加事务管理。 - 类似的项目使用 NHibernate,没有这个问题。
连接字符串如下:
`connectionString="metadata=res:///ResourceAccess.MyDb.csdl|res:///ResourceAccess.MyDb.ssdl|res://*/ResourceAccess.MyDb.msl;provider=System.Data.SqlClient;provider connection string="data source=wil-gvpsqldev01;initial catalog=MyDb;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />`
System.Transactions 的诊断信息为:
<E2ETraceEvent>
    <System>
        <EventID>0</EventID>
        <Type>3</Type>
        <SubType Name="Information">0</SubType>
        <Level>8</Level>
        <TimeCreated SystemTime="2014-10-31T14:39:43.0061489Z" />
        <Source Name="System.Transactions" />
        <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
        <Execution ProcessName="CCS.Host.Console.vshost" ProcessID="64568" ThreadID="13"/>
        <Channel/>
        <Computer>xxx</Computer>
    </System>
        <ApplicationData>
            <TraceData>
                <DataItem>
                    <TraceRecord Severity="Information">
                        <TraceIdentifier>http://msdn.microsoft.com/2004/06/System/Transactions/TransactionCreate</TraceIdentifier>
                        <Description>Transaction Created</Description>
                        <AppDomain>CCS.Host.Console.vshost.exe</AppDomain>
                        <ExtendedData>
                            <TraceSource>[Lightweight]</TraceSource>
                            <TransactionTraceIdentifier>
                            <TransactionIdentifier>2e0814b3-7dd2-4c05-ad69-c3787d95c208:1</TransactionIdentifier>
                            <CloneIdentifier>1</CloneIdentifier>
                            </TransactionTraceIdentifier>
                        </ExtendedData>
                    </TraceRecord>
                </DataItem>
            </TraceData>
        </ApplicationData>
</E2ETraceEvent>
<E2ETraceEvent>
    <System>
        <EventID>0</EventID>
        <Type>3</Type>
        <SubType Name="Information">0</SubType>
        <Level>8</Level>
        <TimeCreated SystemTime="2014-10-31T14:39:43.0181489Z" />
        <Source Name="System.Transactions" />
        <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
        <Execution ProcessName="CCS.Host.Console.vshost" ProcessID="64568" ThreadID="13"/>
        <Channel/>
        <Computer>xxx</Computer>
    </System>
    <ApplicationData>
        <TraceData>
            <DataItem>
                <TraceRecord Severity="Information">
                <TraceIdentifier>http://msdn.microsoft.com/2004/06/System/Transactions/TransactionScopeCreated</TraceIdentifier>
                <Description>TransactionScope Created</Description>
                <AppDomain>CCS.Host.Console.vshost.exe</AppDomain>
                <ExtendedData>
                    <TraceSource>[Base]</TraceSource>
                    <TransactionTraceIdentifier>
                    <TransactionIdentifier>2e0814b3-7dd2-4c05-ad69-c3787d95c208:1</TransactionIdentifier>
                    <CloneIdentifier>2</CloneIdentifier>
                    </TransactionTraceIdentifier>
                    <TransactionScopeResult>CreatedTransaction</TransactionScopeResult>
                </ExtendedData>
                </TraceRecord>
            </DataItem>
        </TraceData>
    </ApplicationData>
</E2ETraceEvent>
<E2ETraceEvent>
    <System>
        <EventID>0</EventID>
        <Type>3</Type>
        <SubType Name="Information">0</SubType>
        <Level>8</Level>
        <TimeCreated SystemTime="2014-10-31T14:39:49.1921489Z"/>
        <Source Name="System.Transactions"/>
        <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}"/>
        <Execution ProcessName="CCS.Host.Console.vshost" ProcessID="64568" ThreadID="13"/>
        <Channel/>
        <Computer>ccc</Computer>
    </System>
    <ApplicationData>
        <TraceData>
            <DataItem>
                <TraceRecord Severity="Information">
                    <TraceIdentifier>http://msdn.microsoft.com/2004/06/System/Transactions/Enlistment</TraceIdentifier>
                    <Description>Enlistment Created</Description>
                    <AppDomain>CCS.Host.Console.vshost.exe</AppDomain>
                    <ExtendedData>
                        <TraceSource>[Lightweight]</TraceSource>
                        <EnlistmentTraceIdentifier>
                            <ResourceManagerId>00000000-0000-0000-0000-000000000000</ResourceManagerId>
                            <TransactionTraceIdentifier>
                                <TransactionIdentifier>2e0814b3-7dd2-4c05-ad69-c3787d95c208:1</TransactionIdentifier>
                                <CloneIdentifier>2</CloneIdentifier>
                            </TransactionTraceIdentifier>
                            <EnlistmentIdentifier>0</EnlistmentIdentifier>
                        </EnlistmentTraceIdentifier>
                        <EnlistmentType>PromotableSinglePhase</EnlistmentType>
                        <EnlistmentOptions>None</EnlistmentOptions>
                    </ExtendedData>
                </TraceRecord>
            </DataItem>
        </TraceData>
    </ApplicationData>
</E2ETraceEvent>
<E2ETraceEvent >
    <System >
        <EventID>0</EventID>
        <Type>3</Type>
        <SubType Name="Error">0</SubType>
        <Level>2</Level>
        <TimeCreated SystemTime="2014-10-31T14:39:50.8941489Z" />
        <Source Name="System.Transactions" />
        <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
        <Execution ProcessName="CCS.Host.Console.vshost" ProcessID="64568" ThreadID="13" />
        <Channel/>
        <Computer>ccc</Computer>
    </System>
    <ApplicationData>
        <TraceData>
            <DataItem>
                <TraceRecord  Severity="Error">
                    <TraceIdentifier>http://msdn.microsoft.com/2004/06/System/Transactions/TransactionException</TraceIdentifier>
                    <Description>TransactionException Thrown</Description>
                    <AppDomain>CCS.Host.Console.vshost.exe</AppDomain>
                    <ExtendedData xmlns="http://schemas.microsoft.com/2004/03/Transactions/TransactionExceptionTraceRecord">
                        <TraceSource>[Distributed]</TraceSource>
                        <ExceptionMessage>The partner transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D025)</ExceptionMessage>
                    </ExtendedData>
                </TraceRecord>
            </DataItem>
        </TraceData>
    </ApplicationData>
</E2ETraceEvent>
<E2ETraceEvent >
    <System >
        <EventID>0</EventID>
        <Type>3</Type>
        <SubType Name="Warning">0</SubType>
        <Level>4</Level>
        <TimeCreated SystemTime="2014-10-31T14:39:50.9591489Z" />
        <Source Name="System.Transactions" />
        <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
        <Execution ProcessName="CCS.Host.Console.vshost" ProcessID="64568" ThreadID="13" />
        <Channel/>
        <Computer>ccc</Computer>
    </System>
    <ApplicationData>
        <TraceData>
            <DataItem>
                <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Warning">
                    <TraceIdentifier>http://msdn.microsoft.com/2004/06/System/Transactions/EnlistmentCallbackNegative</TraceIdentifier>
                    <Description>Enlistment Callback Negative</Description>
                    <AppDomain>CCS.Host.Console.vshost.exe</AppDomain>
                    <ExtendedData >
                        <TraceSource>[Lightweight]</TraceSource>
                        <EnlistmentTraceIdentifier>
                        <ResourceManagerId>00000000-0000-0000-0000-000000000000</ResourceManagerId>
                        <TransactionTraceIdentifier>
                            <TransactionIdentifier>2e0814b3-7dd2-4c05-ad69-c3787d95c208:1</TransactionIdentifier>
                            <CloneIdentifier>2</CloneIdentifier>
                        </TransactionTraceIdentifier>
                        <EnlistmentIdentifier>0</EnlistmentIdentifier>
                        </EnlistmentTraceIdentifier><EnlistmentCallback>Aborted</EnlistmentCallback>
                    </ExtendedData>
                </TraceRecord>
            </DataItem>
        </TraceData>
    </ApplicationData>
</E2ETraceEvent>
<E2ETraceEvent >
    <System >
        <EventID>0</EventID>
        <Type>3</Type>
        <SubType Name="Warning">0</SubType>
        <Level>4</Level>
        <TimeCreated SystemTime="2014-10-31T14:39:50.9601489Z" />
        <Source Name="System.Transactions" />
        <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
        <Execution ProcessName="CCS.Host.Console.vshost" ProcessID="64568" ThreadID="13" />
        <Channel/>
        <Computer>ccc</Computer>
    </System>
    <ApplicationData>
        <TraceData>
            <DataItem>
                <TraceRecord Severity="Warning">
                    <TraceIdentifier>http://msdn.microsoft.com/2004/06/System/Transactions/TransactionAborted</TraceIdentifier>
                    <Description>Transaction Aborted</Description>
                    <AppDomain>CCS.Host.Console.vshost.exe</AppDomain>
                    <ExtendedData >
                        <TraceSource>[Lightweight]</TraceSource>
                        <TransactionTraceIdentifier>
                        <TransactionIdentifier>2e0814b3-7dd2-4c05-ad69-c3787d95c208:1</TransactionIdentifier>
                        </TransactionTraceIdentifier>
                    </ExtendedData>
                </TraceRecord>
            </DataIt

这个表上有任何触发器吗? - Keith Payne
哇,我甚至没有想到那个。刚刚检查了一下,没有触发器。 - Eric Scherrer
1个回答

4
问题出在EF在获取groupId的实体后关闭连接,然后再删除这些实体。这会引起升级。解决方法是控制连接的打开和关闭:

http://msdn.microsoft.com/en-us/data/dn456849.aspx

EF6及以后版本的行为

对于EF6及以后的版本,我们采取的方法是如果调用代码通过调用context.Database.Connection.Open()打开连接,则它有充分的理由这样做,框架将认为它想要控制打开和关闭连接,并且不再自动关闭连接。

我找到的唯一关于此行为的文档是这篇博客文章中的表格,暗示了早于2008年的SQL Server版本会在存在多个连接时进行升级:

https://petermeinl.wordpress.com/2011/03/13/avoiding-unwanted-escalation-to-distributed-transactions/

这就是我的结论:

using System;
using System.Data;

namespace Services.ResourceAccess
{
    public class ResourceAccess : IDisposable
    {
        private readonly Lazy<MyDbEntities> _context;

        public ResourceAccess()
        {
            _context = new Lazy<MyDbEntities>(() =>
            {
                var context = new MyDbEntities();

                context.Database.Connection.Open();

                return context;
            });
        }

        public void DeleteGroupDetails(int groupId)
        {
            var deleted = _context.Value.tblGroupDetails.Where(x => x.GroupID == groupId);

            _context.Value.tblGroupDetails.RemoveRange(deleted);

            _context.Value.SaveChanges();
        }

        public void Dispose()
        {
            if (_context.IsValueCreated)
            {
                if (_context.Value.Database.Connection.State == ConnectionState.Open)
                {
                    _context.Value.Database.Connection.Close();
                }
            }
        }
    }
}

1
我从未见过EF引起MSDTC出现这种行为。在一个TransactionScope中多次打开和关闭上下文连接应该是完全可行的。但是另一个问题是:当只有一个SaveChanges调用时,为什么要使用TransactionScope? - Gert Arnold
@GertArnold 很好的问题。关于TS,如果您使用内置的Tx管理,则此代码最终将存在于使用TS的WCF中。一旦我使其工作,我就将TS从此方法中移出到调用方法中,并调用另一个具有另一个SaveChanges的方法。当我遇到问题时,我尝试尽可能简化它以找到根本原因,而上面的代码是我能够得到的最简单的失败代码。关于您的第一个问题,我会更新答案。 - Eric Scherrer

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