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,
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>
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. Transaction
Flow
Option.
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 Invalid
Operation
Exception
if the service has at least one
incompatible endpoint. Transaction
Flow
Option.
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 Transaction
Flow
Option.
NotAllowed
:
//Invalid definition: [ServiceContract] interface IMyContract { [OperationContract(IsOneWay =true
)] [TransactionFlow(TransactionFlowOption.Allowed
)] void MyMethod(); }
3.22.27.45