Propagating a Transaction Across AppDomains

April 30, 2010

2 comments

Earlier this week I had to propagate a System.Transactions transaction across .NET AppDomains. If you use a TransactionScope and rely on the ambient transaction propagation, you’ll find that the transaction does not cross the AppDomain boundary. This actually makes sense from an isolation perspective—transactions represent an isolation boundary and have their own failure semantics, while AppDomains represent another isolation boundary and have other failure semantics.

There are several ways to propagate a transaction across AppDomains, but bear in mind that regardless of the method you choose, the transaction will be promoted to a distributed transaction when it flows across AppDomains. This means that if you’re relying on a lightweight resource manager that can’t handle distributed transactions, you’ll have to seek other workarounds.

The easiest way to pass a transaction across the AppDomain boundary is to pass the Transaction instance to the method that resides in another AppDomain. In the other AppDomain, you can create a new TransactionScope and instruct it to use that transaction.

Here’s an example of a method that uses this technique inside another AppDomain:

 class  RunsInAnotherAppDomain  : MarshalByRefObject 
 {
   public  void  Method1(Transaction  txToUse)
   {
     using  (TransactionScope  scope = new  TransactionScope (txToUse))
     {
       TransactionInformation  txInfo = Transaction.Current.TransactionInformation;
       Console.WriteLine("\tSecondary AppDomain:" );
       Console.WriteLine("\t\tLocal identifier: "  + txInfo.LocalIdentifier);
       Console.WriteLine("\t\tDistributed identifier: "  + txInfo.DistributedIdentifier);
 
       Transaction.Current.Rollback();
     }
   }
 }

Another way is to retrieve a transaction propagation token, which is a byte[], and pass it to the method that resides in another AppDomain. In the first AppDomain you’ll have to use the TransactionInterop.GetTransmitterPropagationToken method and in the second AppDomain you’ll have to use the TransactionInterop.GetTransactionFromTransmitterPropagationToken method.

Here’s an example of a method that uses a transaction propagation token inside another AppDomain:

 class  RunsInAnotherAppDomain  : MarshalByRefObject 
 {
   public  void  Method2(byte [] txToken)
   {
     Transaction  txToUse = 
       TransactionInterop.GetTransactionFromTransmitterPropagationToken(txToken);
     using  (TransactionScope  scope = new  TransactionScope (txToUse))
     {
       TransactionInformation  txInfo = Transaction.Current.TransactionInformation;
       Console.WriteLine("\tSecondary AppDomain:" );
       Console.WriteLine("\t\tLocal identifier: "  + txInfo.LocalIdentifier);
       Console.WriteLine("\t\tDistributed identifier: "  + txInfo.DistributedIdentifier);
 
       Transaction.Current.Rollback();
     }
   }
 }
 

Finally, you can rely on WCF to pass the transaction for you—if your cross-AppDomain communication does not use Remoting (MarshalByRefObject) but uses WCF, you can configure WCF transaction flow and use that to promote and propagate your transaction.

You can download the complete example (a Visual Studio 2010 solution) here. This is the sample output with two transactions that are propagated to the secondary AppDomain and aborted inside it. (As you can see, the distributed transaction identifier appears after the transaction is passed to the secondary AppDomain.)

Passing Transaction object:

        Main AppDomain, before cross-domain call:

                Local identifier: f31f3e8b-c78d-43a9-8642-28c987bfeb3a:1

                Distributed identifier: 00000000-0000-0000-0000-000000000000

        Secondary AppDomain:

                Local identifier: f0ac61d7-430d-481c-a559-544f9d0de6d7:1

                Distributed identifier: 02700344-be0e-404b-bab3-cfb5926301e7

        Main AppDomain, after cross-domain call:

                Local identifier: f31f3e8b-c78d-43a9-8642-28c987bfeb3a:1

                Distributed identifier: 02700344-be0e-404b-bab3-cfb5926301e7

The transaction has aborted.

Passing transaction propagation token:

        Main AppDomain, before cross-domain call:

                Local identifier: f31f3e8b-c78d-43a9-8642-28c987bfeb3a:2

                Distributed identifier: 00000000-0000-0000-0000-000000000000

        Secondary AppDomain:

                Local identifier: f0ac61d7-430d-481c-a559-544f9d0de6d7:2

                Distributed identifier: 5a182389-9d9b-4b91-a1f4-12af1a454586

        Main AppDomain, after cross-domain call:

                Local identifier: f31f3e8b-c78d-43a9-8642-28c987bfeb3a:2

                Distributed identifier: 5a182389-9d9b-4b91-a1f4-12af1a454586

The transaction has aborted.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

2 comments

  1. ReiskaJune 4, 2010 ב 10:31 AM

    Hi,

    would you happen to know, if it’s possible to roll back the flowed transaction in the initiating app domain? You know, set up a transaction in the first app domain, flow it to the 2nd, commit it on the second, but still abort it in the 1st app domain.

    I tried to create a 2-phase commit, but it seems that the ambient transaction in the first app domain doesn’t know anything about 2nd app domain’s enlistments.

    I’m sure that this is possible with WCF. However, I’m still stuck with remoting for a while.. Is there anything I can do about this?

    Keep up the good work!

    Reply
  2. ToddNovember 27, 2012 ב 5:23 PM

    I found that, if you wanted to do things in the parent as well as child processes, that using TransactionScope in the child would make the parent fail. I had to explicitly enlist the transaction, work with it, and then let the parent commit via its own TransactionScope.

    I posted about this at http://stackoverflow.com/questions/13574245/using-transactions-across-processes/13594119#13594119 .

    Reply