TransactionScope - 底层提供程序在注册事务时失败。MSDTC被中止。

16

我们团队遇到了一个问题,表现为:

在EnlistTransaction上底层提供程序失败;无法访问已处理的对象。对象名称:“Transaction”。

enter image description here

这个问题似乎是在我们开始使用TransactionScope来处理我们应用程序的事务时出现的。

堆栈跟踪的顶部部分捕获如下:

在System.Data.EntityClient.EntityConnection.EnlistTransaction(Transaction transaction) at System.Data.Objects.ObjectContext.EnsureConnection() at System.Data.Objects.ObjectContext.ExecuteStoreCommand(String commandText, Object[] parameters) at Reconciliation.Models.BillLines.BillLines.Reconciliation.Interfaces.IBillLineEntities.ExecuteStoreCommand(String, Object[]) at Reconciliation.Models.Legacy.EntityDbEnvironment.ExecuteOracleSql(String sql) in EntityDbEnvironment.cs: line 41

同时,MSDTC日志也会更新,我使用这里的说明来提取日志信息:

pid=7060       ;tid=7908       ;time=04/29/2013-16:38:30.269   ;seq=136        ;eventid=TRANSACTION_BEGUN                        ;tx_guid=60f6390c-7570-488a-97a9-2c3912c4ca3e     ;"TM Identifier='(null)                                            '" ;"transaction has begun, description :'<NULL>'"
pid=7060       ;tid=7908       ;time=04/29/2013-16:38:30.269   ;seq=137        ;eventid=RM_ENLISTED_IN_TRANSACTION               ;tx_guid=60f6390c-7570-488a-97a9-2c3912c4ca3e     ;"TM Identifier='(null)                                            '" ;"resource manager #1002 enlisted as transaction enlistment #1. RM guid = 'defc4277-47a6-4cd9-b092-93a668e2097b'"
pid=7060       ;tid=7908       ;time=04/29/2013-16:38:31.658   ;seq=138        ;eventid=RECEIVED_ABORT_REQUEST_FROM_BEGINNER     ;tx_guid=60f6390c-7570-488a-97a9-2c3912c4ca3e     ;"TM Identifier='(null)                                            '" ;"received request to abort the transaction from beginner"
pid=7060       ;tid=7908       ;time=04/29/2013-16:38:31.658   ;seq=139        ;eventid=TRANSACTION_ABORTING                     ;tx_guid=60f6390c-7570-488a-97a9-2c3912c4ca3e     ;"TM Identifier='(null)                                            '" ;"transaction is aborting"
pid=7060       ;tid=7908       ;time=04/29/2013-16:38:31.658   ;seq=140        ;eventid=RM_ISSUED_ABORT                          ;tx_guid=60f6390c-7570-488a-97a9-2c3912c4ca3e     ;"TM Identifier='(null)                                            '" ;"abort request issued to resource manager #1002 for transaction enlistment #1"
pid=7060       ;tid=7908       ;time=04/29/2013-16:38:31.658   ;seq=141        ;eventid=RM_ACKNOWLEDGED_ABORT                    ;tx_guid=60f6390c-7570-488a-97a9-2c3912c4ca3e     ;"TM Identifier='(null)                                            '" ;"received acknowledgement of abort request from the resource manager #1002 for transaction enlistment #1"
pid=7060       ;tid=7908       ;time=04/29/2013-16:38:31.658   ;seq=142        ;eventid=TRANSACTION_ABORTED                      ;tx_guid=60f6390c-7570-488a-97a9-2c3912c4ca3e     ;"TM Identifier='(null)                                            '" ;"transaction has been aborted"

正如您所看到的,在 RM_ENLISTED_IN_TRANSACTION 记录后一秒,出现了一个 RECEIVED_ABORT_REQUEST_FROM_BEGINNER

我们无法理解这个中止请求从何处发起,或者为什么会触发它。引起问题的SQL是一个简单的SELECT查询,我们可以通过数据库客户端执行而不会遇到问题。

该应用程序大多数时间都可以正常工作,只有偶尔出现此问题。

我们使用带有Entity Framework的Oracle 10.2.0.5.0。

更新

根据@Astrotrain的建议,我设置了System.Transactions上的日志记录。 最终产生的条目被截断了一半:

....
<ApplicationData>
<TraceData>
<DataItem>
<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Information">
<TraceIdentifier>http://msdn.microsoft.com/2004/06/System/Transactions/TransactionScopeCreated</TraceIdentifier>
<Description>TransactionScope Created</Description>
<AppDomain>BillLineGeneratorUI.exe</AppDomain>
<ExtendedData xmlns="http://schemas.microsoft.com/2004/03/Transactions/TransactionScopeCreatedTraceRecord">
<TraceSource>[Base]

正如您所看到的,异常实际上阻止了日志完成。我可以从中学到什么?有任何想法吗?


你如何启动事务?它们是嵌套的吗?你能发一些相关代码吗?我暂时排除在两个参与事务的MSDTC实例之间的可达性问题。 - Marcel N.
3
不用考虑之前的事情了,请看这个链接:http://blogs.msdn.com/b/ajit/archive/2010/05/31/msdtc-oracle-10g-client-new-transaction-cannot-enlist-in-the-specified-transaction-coordinator.aspx - Marcel N.
中止时间超过了1秒钟。也许您的配置自动/连接/命令/事务超时设置为1秒钟(或1000毫秒),而不是合理的时间。 - Simon Mourier
你使用的是哪个EF版本?你使用的是哪个ODAC版本?为什么需要创建自己的作用域?它以前有用过吗?当你调用SaveChanges方法时,dbcontext会自动创建一个作用域。 - Justin
@marceln - 谢谢,但应用程序大部分时间都能正常工作,只是偶尔会出现问题。 - m.edmondson
显示剩余3条评论
4个回答

7

不要使用我认为难以使用的MSDTC跟踪工具,我建议使用System.Transactions跟踪源 - 只需在web.config中包含以下内容:
如果您使用SvcTraceViewer.exe打开日志文件,您将得到一种漂亮的步骤可视化表示。

<configuration>
  <system.diagnostics>
   <trace autoflush="true" />
   <sources>
     <source name="System.Transactions" switchValue="Information">
       <listeners>
         <add name="tx"
              type="System.Diagnostics.XmlWriterTraceListener"
              initializeData="C:\MyApp-transactions-log.svclog" />
       </listeners>
     </source>
   </sources>
  </system.diagnostics>
</configuration>

这不是解决方案,但它可能会为您提供有关出现问题的更多信息。


好主意。文档似乎非常针对WCF,但我认为它仍然适用吧? - m.edmondson
我认为这不是WCF特定的,System.Transactions只是一个常规的TraceSource。XmlWriterTraceListener生成的输出格式似乎是相同的,但我确信你不需要将其与WCF应用程序结合使用。 - Leon Bouquiet
我已经成功使用这种方法来记录异常发生的时刻,它实际上会在日志中断(标签中间)停止。我已经更新了问题并提供了更多信息。 - m.edmondson
假设您使用了“<trace autoflush =”true“/>”,我能想到的唯一获取更完整日志消息的方法是使用其他跟踪侦听器之一,例如请参见http://weblogs.asp.net/ralfw/code-instrumentation-with-tracesource-my-personal-vade-mecum - Leon Bouquiet

2

你提到“该应用程序大部分时间都可以正常工作,只是偶尔会出现这个问题。” 由此我们可以得出结论,提供商支持分布式事务,原因是连接或处理中的其他间歇性故障。

  • 请问您是否使用嵌套事务范围?如果由于某些原因,内部范围被回滚(在不调用complete的情况下被释放),将立即回滚外部事务导致错误。使用TransactionScopeOption.Required(RequiresNew也可以,但RequiresNew还有其他与死锁相关的问题,因此最好使用Required)来解决此问题。

  • 否则,可能是在事务处于活动状态时进行了其他操作,并且为了解决此问题,请使用IsolationLevel = IsolationLevel.ReadCommitted。


1
我们也遇到了这个问题。解决它的最好方法似乎是打开一个新的数据库连接,然后只在事务中执行必要的操作。尽可能使事务保持小的规模总是有益的。
 DatabaseContext db1 = new DatabaseContext();

 doSomeDatabaseActions(db1);

 TransactionOptions transOpts = new TransactionOptions();
 transOpts.IsolationLevel = System.Transactions.IsolationLevel.Serializable;

 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, transOpts))
 {
     using (DatabaseContext db2 = new DatabaseContext())
     {
        doDatabaseChecksWithLock(db2);
        doChanges(db2);
        db2.SaveChanges();
     }

     scope.Complete();
  }

我们在不引入第二个连接的情况下遇到了这个问题。请注意,如果扩大事务(将doSomeDatabaseActions作为事务的一部分),即使使用1个连接(db1),错误也会消失。

1
这可能应该是一条注释,但它太大了。我希望它有所帮助。
嵌套事务中最常见的错误之一是:
using(TransactionScope outerScope = new TransactionScope())
{
    // Execute query 1

    using(TransactionScope innerScope = new TransactionScope())
    {
        try
        {
            // Execute query 2
        }
        catch (Exception)
        {
        }

        innerScope.Complete();
    }

    outerScope.Complete();
}

现在,如果try/catch块内的查询2引发错误,您将在try/catch块中捕获异常并处理它,但是这里有一个意外,当您尝试完成事务时,应用程序将在第15行抛出ObjectDisposedException。这是因为DTC已经捕获了异常,尽管您已经处理了它,但.Net代码已经处理了TransactionScope对象并且事务已被回滚。请注意,我已经说过“对象”,这是因为两个TransactionScope对象都已被处理,因为它们是同一事务的一部分。

我该如何处理/防止出现这种情况? - m.edmondson

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