Generic Message Contract and Transaction Flow in WCF

OK, this one had me fistpumping (in the air!) for quite some time.

Short introduction. For my upcoming session at the Sela Developer Practice, I’m developing a demo that involves durable content based routing with MSMQ and WCF. I was using a generic message contract:

[ServiceContract(Namespace = ”SomeNamespace”, SessionMode = SessionMode.Allowed)]
public interface ITwoWay
{
    [TransactionFlow(TransactionFlowOption.Allowed)]
    [OperationContract(AsyncPattern = true, IsOneWay = false, Action = "*", ReplyAction = "*")]
    IAsyncResult BeginProcessRequest(Message message, AsyncCallback callback, object state);
    Message EndProcessRequest(IAsyncResult result);
}

N.B. Apart from the name and namespace, this is identical to the WCF 4.0 RoutingService IRequestReplyRouter interface.

Furthermore I was using:

  • WsHttp binding with transactionFlow=”true”,
  • [OperationBehavior(TransactionFlowOption.Allowed)] on both the client generic message contract and the typed service contract,
  • [OperationBehavior(TransactionScopeRequired = true)] on the service method,
  • and a TransactionScope in the client to make sure the WCF call was performed from within an ambient transaction.

I was experiencing a strange issue with flowing the transaction from the client to the service. The transaction SOAP header was not added, even though I was following all the rules to enable transaction flow described above.

It turns out there’s a known issue (described here) with flowing transactions when using a generic message contract. After a lot of research, I found that this is because of the fact that a generic message contract uses wildcard specifications for the Action and/or ReplyAction. Then I wondered how the .NET 4.0 RoutingService would deal with this issue, so I reviewed the code of the .NET 4.0 RoutingService and found this:

private static void ConfigureTransactionFlow(ServiceEndpoint endpoint)
{
    CustomBinding binding = endpoint.Binding as CustomBinding;
    if (binding == null)
    {
        binding = new CustomBinding(endpoint.Binding);
    }

    TransactionFlowBindingElement element = binding.Elements.Find<TransactionFlowBindingElement>();
    if (element != null)
    {
        element.AllowWildcardAction = true;
        endpoint.Binding = binding;
    }
}

Applying this change to the binding configuration fixes the transaction flow when using a generic message contract.

There’s one more gotcha though; if you apply this to the endpoint of the channel (or any class derived from ClientBase<T>), it still doesn’t work, because the channel has already been initialized internally before you get to change the binding configuration. You need to apply this to the endpoint of the ChannelFactory, BEFORE you create the channel. (I use a customized base class (other than ClientBase<T>) for WCF proxies where I arrange for a timely initialization of the binding configuration).

Sela

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