Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Propagating a Transaction Across AppDomains

DZone's Guide to

Propagating a Transaction Across AppDomains

·
Free Resource

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.

 

Topics:

Published at DZone with permission of Sasha Goldshtein, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}