Is it possible to create a TransactionScope in the WCF service user behavior? (async, wait, TransactionScopeAsyncFlowOption.Enabled)

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(); // Transaction.Current is now null 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(); 
+5
source share
1 answer

It turns out that we should not use the async / await keyword on the server along with distributed transactions, see this blog post .

0
source

Source: https://habr.com/ru/post/1240531/


All Articles