Is it possible to create a TransactionScope in a Custom WCF Service Behavior? (async, await, TransactionScopeAsyncFlowOption.Enabled)

北城以北 提交于 2020-01-12 07:25:48

问题


TL;DR ?

Screencast explaining problem: https://youtu.be/B-Q3T5KpiYk

Problem

When flowing a transaction from a client to a service Transaction.Current becomes null after awaiting a service to service call.

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 isn't enabled by default I don't know, but I don't like to repeat myself so I figured I'd always create an inner transactionscope with that option using a custom behavior.

Problem UPDATE

It doesn't even have to be a service to service call, an await to a local async method also nulls Transaction.Current. To clearify with an example

[OperationBehavior(TransactionScopeRequired = true)]
public async Task CallAsync()
{
    await WriteAsync();
    // Transaction.Current is now null
    await WriteAsync();                     
}

Attempted Solution

I created a Message Inspector, implementing IDispatchMessageInspector and attached it as a service behavior, code executes and everyting no problem there, but it doesn't have the same effect as declaring the transactionscope in the 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();
        }
    }
}

by looking at the identifiers when debugging I can see that it in fact is the same transaction in the message inspector as in the service but after the first call, i.e.

await _service_WriteAsync();

Transaction.Current becomes null. Same thing if not getting the current transaction from OperationContext.Current in the message inspector as well so it's unlikely that is the problem.

Question

Is it even possible to accomplish this? It appears like the only way is to declare a TransactionScope in the service method, that is:

public async Task CallAsync()
{
    var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
    await _service.WriteAsync();
    await _service.WriteAsync();            
    scope.Complete();
}

with the following service contract it's obvious that we get an exception on the second service call if transaction.current became null inbetween

[OperationContract, TransactionFlow(TransactionFlowOption.Mandatory)]
Task WriteAsync();

回答1:


Turns out we shouldn't really be using the async/await keyword on the server together with distributed transactions, see this blog post for details.



来源:https://stackoverflow.com/questions/34767978/is-it-possible-to-create-a-transactionscope-in-a-custom-wcf-service-behavior-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!