WCF can propagate transactions across the service boundary. This enables a service to participate in the client’s transaction, and for 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 whether or not the client transaction is propagated to the service. I call a transaction-aware
binding any binding that is capable of propagating the client’s transaction to the service if configured to do so. Not all bindings are transaction-aware; only the TCP-, IPC- and WS-related bindings are transaction-aware (those would be the NetTcpBinding
, the NetNamedPipeBinding
, the WSHttpBinding
, the WSDualHttpBinding
and the WSFederationHttpBinding
, respectively).
By default, transaction-aware bindings do not propagate transactions. The reason is that like most everything else in WCF, it 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 at the binding on both the service host and the 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>
Note that the value of the TransactionFlow
property is not published in the service metadata. If you use Visual Studio 2005 or SvcUtil to generate the client config file, you will still need to manually enable or disable transaction flow as required.
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 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 that 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, and 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(...)
{...}
}
But such use is discouraged because it splits the definition of the logical service contract that will be published.
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 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(...);
}
When the operation is configured to allow transaction flow by providing TransactionFlowOption.Allowed
to the TransactionFlowOption
attribute, if the client has a transaction, the service will allow the client’s transaction to flow across the service boundary. However, the service may or may not use the client’s transaction even though it was propagated. 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 transaction will cause an error because the transaction information in the message will not be understood by the service. 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, and so may elect to set TransactionFlow
to false
in the binding even if the service has it set to true
.
When the operation is configured for TransactionFlowOption.Mandatory
, the service and 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 throws 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. Yet again, the service may or may not use the client’s transaction.
Propagating the client transaction to the service requires, by its very nature, allowing the service to abort the client transaction if so desired. This implies that you cannot flow the client 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 for anything but TransactionFlowOption.NotAllowed
.
//Invalid definition: [ServiceContract] interface IMyContract { [OperationContract(IsOneWay =true
)] [TransactionFlow(TransactionFlowOption.Allowed
)] void MyMethod(...); }