Handling Multiple Services in a WCF Transaction
- Sudherson Vetrichelvan
- Jun 22, 2015
- 2 min read

In this post we'll see how we can handle multiple services(operations) as a single transaction. This needs the client to control the transaction that way the transaction can be comitted only when all the participating services(operation) complete successfully.
Scenario:
Serivce1 -> Operation1, Operation2
Service2 -> Operation3
Now the goal is to make these 3 operations transactional.
[ServiceContract(SessionMode = SessionMode.Required)]//This mandates the Per Session Context that way the //client can use the same context for various calls.
public interface ISerivce1
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)] //This mandates the client to control the transaction //scope.
void Operation1();
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void Operation2();
}
[ServiceContract(SessionMode = SessionMode.Required)]
public interface ISerivce2
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void Operation3();
}
//InstanceContextode is PerSession by default and required for transactions that spans more than one operation.
//TransactionIsolationLevel governs how the database will lock and read for a transaction when multiple threads are //running the service.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
TransactionIsolationLevel = System.Transactions.IsolationLevel.RepeatableRead)]
public class Service1
{
//TransactionScopeRequired says this method will be executed as a transaction in the service and
//TransactionAutoComplete = false says the transaction wont' be commited by the service automatically,
//rather only when 'OperationContext.Current.SetTransactionComplete();' is called in the last Operation to signal commit.
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public void Operation1()
{
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public void Operation2()
{
}
}
public class Service2
{
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public void Operation3()
{
//do operations
OperationContext.Current.SetTransactionComplete(); //This statement commits the transaction.
}
}
//Enable transaction in the bindings
<bindings>
<wsHttpBinding>
<binding name="WSHttpBindingConfig" transactionFlow="true" />
</wsHttpBinding>
</bindings>
Use this binding for both the endpoints (Service1 & Service2 endpoints).
//Client side transaction scope
static void ClientCode()
{
Service1Client client1 = new Service1Client();
Service2Client client2 = new Service2Client();
TransactionOptions tOpts = new TransactionOptions();
tOpts.IsolationLevel = IsolationLevel.RepeatableRead;
tOpts.Timeout = new TimeSpan(0, 1, 0);
using (TransactionScope tx = new TransactionScope(TransactionScopeOption.RequiresNew, tOpts))
{
client1.Operation1();
client1.Operation2();
client2.Operation3();
//This tells the transacion that the scope is complete and ready for commit, yet any exception with the using block will rollback the transaction.
tx.Complete();
}
client1.Close();
client2.Close();
}
****Some caveats****
******Any failure in one of the operations will roll back both the service changes. If the transaction scope are run by individual services then the failure in the client won't have the scope to roll back in a specific service, hence we forced the client transaction scope using the 'TransactionFlowOption.Mandatory' attribute.
******If you call the SetTransactionComplete method, but later fail to call
the Complete method of the TransactionScope object, the transaction will be silently
rolled back.
******SetTransactionComplete method can be called only once in a transaction.