This is the first in a short series of posts in which we’ll debug problems that arise from the use of distributed transactions in WCF. Particularly, we will look into deadlocks, timeouts, and miscellaneous transaction-related issues.
To begin with, here’s a brief recap of what has to be done to flow a transaction across WCF service calls:
- The WCF operation must be decorated with the [TransactionFlow] attribute and the TransactionFlowOption enum. You can either disallow, allow, or mandate transaction flow to the service operation—violations of the contract result in a ProtocolException on the caller’s side.
- The .NET method implementing the operation contact must be decorated with the [OperationBehavior(TransactionScopeRequired=true)] attribute.
- The WCF binding used by both parties must support transaction flow, and have its TransactionFlow property set to true (this enables the transaction flow binding element).
- If the .NET class that implements the service contract is configured with ConcurrencyMode.Multiple and InstanceContextMode.Single, then it must also be configured with ReleaseServiceInstanceOnTransactionComplete=false.
The following is a complete example of a service implementation that supports (but does not mandate) transaction flow:
public interface IBookStore
void PlaceOrder(OrderDTO orderDTO);
InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple,
ReleaseServiceInstanceOnTransactionComplete = false)]
class BookStore : IBookStore
[OperationBehavior(TransactionScopeRequired = true)]
public void PlaceOrder(OrderDTO orderDTO)
using (CrmDataContext context = DataContextFactory.GetContext())
OrderId = Guid.NewGuid(),
Amount = orderDTO.Amount,
Item = orderDTO.Item,
CustomerName = orderDTO.CustomerName,
DiscountRate = 0.0f
To verify that the transaction flows from the client to the service, you can use the WCF Service Trace Viewer after configuring tracing. Alternatively, if you’re still in the debugging phase, look at the Transaction.Current static property in the Immediate Window on both sides (the service and the caller) and make sure that the transaction information is the same.
The TransactionInformation property has a local transaction identifier and a distributed transaction identifier. If the transaction was properly flown across the WCF service call, it will always have a distributed transaction identifier. By the way, transactions exhibit the same auto-promotion behavior when flown across AppDomains, if the Transaction instance is marshaled by the Remoting infrastructure.
Stay tuned for more installments in which we will begin to diagnose some errors caused by the nature of distributed transactions.