Configuration of Transactions for use in WCF

After starting to look into WCF and transactions, I feel it has been unclear how you can flow transactions from the client to the server to enlist in. Prompted by the umpteenth of such discussions, I decided to experiment my way of this. Along the lines of the ATM sample posted earlier, I wrote a small client and a small server. Contrary to last posted sample, this time I am creating everything from code, so no configuration files (I am actually slightly ambivalent to configuration files: For development and test purposes, I like them, but I don't like the idea of end users fiddling with them).

Enough talk. Let's look at the contract definition for the server:

[ServiceContract]

public interface ITransactionServer

{

   [OperationContract]

   [TransactionFlow(TransactionFlowOption.Mandatory)]

   void MandatoryOperation(InvocationInfo invocationInfo);

   [OperationContract]

   [TransactionFlow(TransactionFlowOption.Allowed)]

   void AllowedOperation(InvocationInfo invocationInfo);

}

The operations themselves actually do nothing more than log the existence of an ambient transaction and the nature of that ambient transaction to the console. Only interesting property about the actual implementation of the server methods is that they have the OperationBehavior TransactionScopeRequired property set to true. The service has an endpoint that exposes the functionality using a TCP binding that can flow transactions. Oh, another point: The InvocationInfo is just a simple wrapper around some configuration details that I use on the server to echo some meaningful information to the console.

The client, on the other hand, runs through multiple configurations to test how transactions are sent across the wire. The table below illustrates the configurations. All configurations are based on TCP transport, but depending on the configuration may or may not allow the flow of transactions from the client to the server.

The procedure on the client is as follows: For each of the configurations listed below, the client will first create a binding that differs only from run to run by the presence of a TransactionFlowBindingElement (the “permanent members” of the binding element collection are: ReliableSessionBindingElement, SecurityBindingElement, TextMessageEncodingBindingElement and finally TcpTransportBindingElement). Further, based on the configuration, the client will then create a channel to the service. This will happen within a transaction scope based on the value of the field “Client Supplied Tx” in the table below. Finally, the client will try to invoke both of the operations offered by the (somewhat rich, or…) service. The results can be seen below.

Operation Invoked

Binding Allows Flow of Tx

Client Supplied Tx

Result Client Side

Results Server Side

MandatoryOperation

True

False

ProtocolException

N/A

AllowedOperation

True

False

OK

Local Tx

MandatoryOperation

True

True

OK

Distributed Tx

AllowedOperation

True

True

OK

Distributed Tx

MandatoryOperation

False

False

InvalidOperationException

N/A

AllowedOperation

False

False

InvalidOperationException

N/A

MandatoryOperation

False

True

InvalidOperationException

N/A

AllowedOperation

False

True

InvalidOperationException

N/A

The exceptions encountered in the various configurations above are listed below with type and exception message.

ProtocolException: The service operation requires a transaction to be flowed.

InvalidOperationException: At least one operation on the 'ITransactionServer' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding 'CustomBinding' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement.

Interesting to note, as soon as I changed the declaration of MandatoryOperation from being Mandatory to Allowed, all operations succeeded and the transactions seen on the server side were as follows:

Binding Allows Flow of Tx

Client Supplied Tx

Results Server Side

True

False

Local Tx

True

True

Distributed Tx

False

False

Local Tx

False

True

Local Tx

I have taken out the column that indicates the operation invoked on the server, as the two operations now are identical. Further, I also ripped out the result on the client side, as all operation went through without a hitch.

Well, I think enough has been said (at least by me). It is now time to enjoy the wonderful support we have been given.