Asynchronous transactions and propagation in WCF

I was recently looking into implementing a transactional WCF service asynchronously, and ran into the fact that asynchrony and transactions don’t mix … easily. I also found that there weren’t that many samples of asynchronously implemented transactional WCF services out there that could help to understand the implementation details and play around with, so i decided to write a sample.

The sample contains two WCF services: StorageService and StorageServiceTransactional. As the names suggest, the first doesn’t use transactions, the latter does. The sample uses wsHttpBinding and netMsmqBinding. Below a picture of a call sequence that could be performed with the sample:

AsyncTrans

The sample allows to easily (by (un)commenting only a couple of lines) change the sequence of calls. For instance, you could make the non-transactional StorageService (instead of the transactional StorageServiceTransactional) pick up the message from the MSMQ queue, and see what happens to the transactions and the propagation. The number of possible combinations grows quickly; that’s exactly why this sample can come in handy.

The sample writes the TransactionInformation to the console at several steps during the execution of the call sequences. This allows you to inspect the transaction identifiers, and the transaction propagation. Below a screenshot of the console output, with highlights to the transaction identifiers:

AsyncTrans.ConsoleOutput

In the picture above, you can see that the client and the host used different transactions (different local and distributed identifiers). However, the message exchange between the two services within the host was done within one and the same transaction (same identifiers). The fact that the message exchange was executed ‘within one executable’ doesn’t make too much difference; if you would run two instances of the host executable (and make the appropriate changes regarding the listen urls), the message exchange would still be performed in one transaction. The LocalIdentifier would be different (LocalIdentifier corresponds to AppDomain), but the DistributedIdentifier would be the same.

If you’re wondering about the contents of the SOAP header; below the fragment of the SOAP header (taken with Fiddler2), containing the WS-* header describing the transaction context. You can see that the LocalTransactionIds match the DistributedIdentifiers displayed above:

<CoordinationContext s:mustUnderstand="1" xmlns="http://schemas.xmlsoap.org/ws/2004/10/wscoor" xmlns:mstx="http://schemas.microsoft.com/ws/2006/02/transactions">
  <wscoor:Identifier xmlns:wscoor="http://schemas.xmlsoap.org/ws/2004/10/wscoor">urn:uuid:e49c680d-ebd5-449a-b2a5-36e01b16ac0f</wscoor:Identifier>
  <Expires>59904</Expires>
  <CoordinationType>http://schemas.xmlsoap.org/ws/2004/10/wsat</CoordinationType>
  <RegistrationService>
    <Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">https://bram08r2/WsatService/Registration/Coordinator/Disabled/</Address>
    <ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">
      <mstx:RegisterInfo>
        <mstx:LocalTransactionId>e49c680d-ebd5-449a-b2a5-36e01b16ac0f</mstx:LocalTransactionId>
      </mstx:RegisterInfo>
    </ReferenceParameters>
  </RegistrationService>
  <mstx:IsolationLevel>0</mstx:IsolationLevel>
  <mstx:LocalTransactionId>e49c680d-ebd5-449a-b2a5-36e01b16ac0f</mstx:LocalTransactionId>
  <PropagationToken xmlns="http://schemas.microsoft.com/ws/2006/02/tx/oletx">AQAAAAM...snipped for brevity...AAAA==</PropagationToken>
</CoordinationContext>

Regarding the asynchronous implementation

The WCF services were implemented with the Task Parallel Library, but you could easily swap that out for ‘old-school’ APM style BeginXxx etc. What’s more important is the way the transaction completion is implemented. Asynchronously completing transactions is more complicated, because there are multiple threads involved in the completion process, and you just can’t rely on using (TransactionScope ts = new TransactionScope) { … }. The sample was implemented using the Transaction.Clone() method, which returns a DependentTransaction. This DependentTransaction is used to block the completion process, and it is passed as the asynchronous state, to be completed when the asynchronous call completes (the EndXxx method);

If you find anything wrong with this sample, pls. let me know! Other suggestions are welcome too. You can download it from here.

Hope this helps anyone!

Advertisements

About Bram Veldhoen

I'm an independant senior software development consultant, working mostly in the area of integration using Microsoft BizTalk Server, Windows Communication Foundation and .NET.
This entry was posted in Wcf. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s