TransactionScope - The underlying provider failed on EnlistTransaction. MSDTC being aborted

一笑奈何 提交于 2019-12-03 11:42:21

Instead of using the MSDTC trace tool (which I find horribly spartan), I can recommend using the System.Transactions trace source - just include the following in your web.config:
If you open the log files with SvcTraceViewer.exe you'll get a nice visual representation of the steps.

<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>

Not a solution per se, but this might give you some more information about what goes wrong.

As you mentioned "The application works most of the time, only occasionally displaying this issue." by this we can conclude that the provider does supports distributed transactions and the reason is some other intermittent glitch either in connection or handling.

  • Are you using nested transaction scopes, because if, due to some reason, inner scope is rolled back (disposed without calling complete) that would immediately roll back the outer transaction causing the error. Use TransactionScopeOption.Required (RequiresNew will also do but RequiresNew has other deadlock related issues associated with it so it is better to use Required) to resolve this

  • Otherwise it might be because of some other operation while the transaction is active and to resolve in this case use IsolationLevel = IsolationLevel.ReadCommitted.

We had this problem too. The best way to solve it seems to open a new database connection and just do the things needed in the transaction. Keep transactions as small as possible is always good.

 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();
  }

We had the problem without introducing the 2nd connection. Note that the error was also gone using 1 connection (db1) if the transaction was enlarged (made doSomeDatabaseActions part of the transaction).

This maybe should be a comment but it is too big. I hope it helps.

One of the most tipical error with nested transactions is this:

using(TransactionScope outerScope = new TransactionScope())
{
    // Execute query 1

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

        innerScope.Complete();
    }

    outerScope.Complete();
}

Now if query 2 which is inside of the try/catch block, rasies an error you will catch the exception in the try/catch block and handle it, but here is a supprise the application will throw an ObjectDisposedException on line 15 when you try to complete the transaction. This is because the DTC has already caught the exception and although you have handled it, the TransactionScope objects where already disposed by the .Net code and the transaction has been rolled back. Notice that I have said “objects” this is because both of the TransactionScope object have been disposed as they are a part of the same transaction.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!