Transaction Propagation

WCF can propagate transactions across the service boundary. This enables a service to participate in a client’s transaction, and the client to include operations on multiple services in the same transaction. The client itself may or may not be a WCF service. Both the binding and the operation contract configuration control the decision as to whether or not the client’s transaction is propagated to the service. I call any binding that is capable of propagating the client’s transaction to the service if configured to do so a transaction-aware binding. Only the TCP, IPC, and WS bindings are transaction-aware.

Transaction Flow and Bindings

By default, transaction-aware bindings do not propagate transactions. The reason is that, like most everything else in WCF, this is an opt-in setting: the service host or administrator has to explicitly give its consent to accepting incoming transactions, potentially from across the organization or the business boundaries. To propagate a transaction, you must explicitly enable it in the binding on both the service host and client sides. All transaction-aware bindings offer the Boolean property TransactionFlow, such as:

public class NetTcpBinding : Binding,...
{
   public bool TransactionFlow
   {get;set;}
   //More members
}

TransactionFlow defaults to false. To enable propagation, simply set this property to true, either programmatically or in the host config file. For example, in the case of the TCP binding:

NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.TransactionFlow = true;

or when using a config file:

<bindings>
   <netTcpBinding>
      <binding name = "TransactionalTCP"
         transactionFlow = "true"
      />
   </netTcpBinding>
</bindings>

Transaction Flow and the Operation Contract

Using a transaction-aware binding and even enabling transaction flow does not mean that the service wants to use the client’s transaction in every operation, or that the client necessarily has a transaction to propagate in the first place. Such service-level decisions should be part of the contractual agreement between the client and the service. To that end, WCF provides the TransactionFlowAttribute method attribute, which controls if and when the client’s transaction flows into the service:

public enum TransactionFlowOption
{
   Allowed,
   NotAllowed,
   Mandatory
}

[AttributeUsage(AttributeTargets.Method)]
public sealed class TransactionFlowAttribute : Attribute,IOperationBehavior
{
   public TransactionFlowAttribute(TransactionFlowOption flowOption);
}

Note that the TransactionFlow attribute is a method-level attribute because WCF insists that the decision on transaction flow be made on a per-operation level, not at the service level:

[ServiceContract]
interface IMyContract
{
   [OperationContract]
   [TransactionFlow(TransactionFlowOption.Allowed)]
   void MyMethod();
}

This is deliberate, to enable the granularity of having some methods that use the client’s transaction and some that do not.

The value of the TransactionFlow attribute is included in the published metadata of the service, so when you import a contract definition, the imported definition will contain the configured value. WCF will also let you apply the TransactionFlow attribute directly on the service class implementing the operation:

[ServiceContract]
interface IMyContract
{
   [OperationContract]
   void MyMethod();
}
class MyService : IMyContract
{
   [TransactionFlow(TransactionFlowOption.Allowed)]
   public void MyMethod()
   {...}
}

However, such use is discouraged because it splits the definition of the logical service contract that will be published.

TransactionFlowOption.NotAllowed

When the operation is configured to disallow transaction flow, the client cannot propagate its transaction to the service. Even if transaction flow is enabled at the binding and the client has a transaction, it will be silently ignored and will not propagate to the service. As a result, the service will never use the client’s transaction, and the service and the client can select any binding with any configuration. TransactionFlowOption.NotAllowed is the default value of the TransactionFlowOption attribute, so these two definitions are equivalent:

[ServiceContract]
interface IMyContract
{
   [OperationContract]
   void MyMethod();
}

[ServiceContract]
interface IMyContract
{
   [OperationContract]
   [TransactionFlow(TransactionFlowOption.NotAllowed)]
   void MyMethod();
}

TransactionFlowOption.Allowed

When the operation is configured to allow transaction flow, if the client has a transaction, the service will allow the client’s transaction to flow across the service boundary. However, just because the client propagates its transaction doesn’t mean the service will necessarily use it. When you choose TransactionFlowOption.Allowed, the service can be configured to use any binding, be it transaction-aware or not, but the client and the service must be compatible in their binding configuration. In the context of transaction flow, “compatible” means that when the service operation allows transaction flow but the binding disallows it, the client should also disallow it in the binding on its side: trying to flow the client’s transaction will cause an error, because the service will not understand the transaction information in the message. However, when the service-side binding configuration is set to allow transaction flow, the client may or may not want to enable propagation on its side, so it may elect to set TransactionFlow to false in the binding even if the service has it set to true.

TransactionFlowOption.Mandatory

When the operation is configured with TransactionFlowOption.Mandatory, the service and the client must use a transaction-aware binding with transaction flow enabled. WCF verifies this requirement at the service load time and throws an InvalidOperationException if the service has at least one incompatible endpoint. TransactionFlowOption.Mandatory means the client must have a transaction to propagate to the service. Trying to call a service operation without a transaction results in a FaultException on the client side stating that the service requires a transaction. With mandatory flow, the client’s transaction always propagates to the service, but again, the service may or may not use the client’s transaction.

Warning

The test client WcfTestClient.exe discussed in Chapter 1 does not support mandatory transaction flow. It does not create a transaction on the client side, and therefore will fail all calls to an operation that mandates transaction flow.

One-Way Calls

Propagating the client’s transaction to the service requires, by its very nature, allowing the service to abort the client’s transaction if so desired. This implies that you cannot flow the client’s transaction to a service over a one-way operation, because that call does not have a reply message. WCF validates this at the service load time and will throw an exception when a one-way operation is configured with anything but TransactionFlowOption.NotAllowed:

//Invalid definition:
[ServiceContract]
interface IMyContract
{
   [OperationContract(IsOneWay = true)]
   [TransactionFlow(TransactionFlowOption.Allowed)]
   void MyMethod();
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.27.45