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.
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 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>
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.
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( );
}
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
.
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.
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.
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( ); }
3.15.231.194