TL DR?
Problem with explaining Screencast: https://youtu.be/B-Q3T5KpiYk
Problem
When a transaction moves from the client to the Transaction.Current service, it becomes null after waiting for the service to call the service.
Unless, of course, you create a new TransactionScope in your service method as follows:
[OperationBehavior(TransactionScopeRequired = true)] public async Task CallAsync() { using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await _service.WriteAsync(); await _service.WriteAsync(); scope.Complete(); } }
Why TransactionScopeAsyncFlowOption is not enabled by default, I donβt know, but I donβt like repeating myself, so I decided that I always create an internal transaction with this option using custom behavior.
UPDATE problem
It does not even have to be a service to call a service, waiting for a local asynchronous method is also not valid for Transaction.Current. To clear the example
[OperationBehavior(TransactionScopeRequired = true)] public async Task CallAsync() { await WriteAsync();
Attempt to solve
I created the Message Inspector by implementing IDispatchMessageInspector and binding it as a service behavior, the code executes and has no problems there, but it does not have the same effect as declaring a transaction in a service method.
public class TransactionScopeMessageInspector : IDispatchMessageInspector { public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { var transactionMessage = (TransactionMessageProperty)OperationContext.Current.IncomingMessageProperties["TransactionMessageProperty"]; var scope = new TransactionScope(transactionMessage.Transaction, TransactionScopeAsyncFlowOption.Enabled); return scope; } public void BeforeSendReply(ref Message reply, object correlationState) { var transaction = correlationState as TransactionScope; if (transaction != null) { transaction.Complete(); transaction.Dispose(); } } }
looking at the identifiers during debugging, I see that this is actually the same transaction in the message inspector as in the service , but after the first call, that is
await _service_WriteAsync();
Transaction.Current becomes null . The same thing if you do not get the current transaction from OperationContext.Current in the message inspector, so it is unlikely that this is a problem.
Question
Is it possible to do this? It seems that the only way is to declare a TransactionScope in the service method, i.e.:
public async Task CallAsync() { var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); await _service.WriteAsync(); await _service.WriteAsync(); scope.Complete(); }
with the following service contract, it is obvious that we get an exception in the second service call if transaction.current becomes null inbetween
[OperationContract, TransactionFlow(TransactionFlowOption.Mandatory)] Task WriteAsync();