在WebAPI中为异步操作中的TransactionScope使用自定义IHttpActionInvoker

时间:2016-12-12 15:56:01

标签: c# azure async-await asp.net-web-api2 transactionscope

我想为每个异步控制器操作应用TransactionScope。我不想在每个动作中都这样做,而是在中心位置进行,以便它适用于所有动作。我尝试创建一个继承自ApiControllerActionInvoker的自定义IHttpActionInvoker

public class ControllerActionTransactionInvoker : ApiControllerActionInvoker
{
    public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
    {

        using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            Task<HttpResponseMessage> result;

            result = base.InvokeActionAsync(actionContext, cancellationToken);

            scope.Complete();

            return result;
        }

    }
}

然后在Web Api的Startup类中用新创建的一个替换默认的IHttpActionInvoker

config.Services.Replace(typeof(IHttpActionInvoker), new ControllerActionTransactionInvoker());

现在,我可以调用控制器操作并获得结果,但是我手动为一系列Db操作引发了异常,并且所需的事务处理过程不起作用。所以在DB中完成了部分工作。 在Azure api应用程序中托管api之后它根本不起作用。它说没有找到控制器动作。

如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

您的scope正在完成并在控制器操作执行完毕之前被处理,因为您没有等待响应。请尝试这样:

public class ControllerActionTransactionInvoker : ApiControllerActionInvoker
{
    public override async Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
    {
        using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            HttpResponseMessage result = await base.InvokeActionAsync(actionContext, cancellationToken);
            scope.Complete();
            return result;
        }
    }
}

虽然这可能很有效,但您可能还想考虑在Web API堆栈中更高层次 - 可能在处理程序中 - 因为您可能会错过其他事务活动,例如处理程序,过滤器等取决于您想要进出的范围。