覆盖Web API ApiControllerActionInvoker导致方法在抛出异常时运行两次

时间:2016-07-06 14:50:12

标签: c# multithreading asynchronous asp.net-web-api asp.net-web-api2

我最近遇到了一个问题,我正在开发一个API,它在某些方法中与两个数据源进行了对话。一对方法的POST通过使用实体框架修改SQL数据以及使用基于STA COM的旧SDK的数据源。为了使STA COM SDK代码在API方法中正常工作,我必须创建方法属性,将方法标识为需要单线程。我通过覆盖ApiControllerActionInvoker中的InvokeActionAsync()方法强制进行单线程处理。如果没有为某个方法赋予单线程属性,则重写的调用者只使用普通的基类InvokeActionAsync()。

public class SmartHttpActionInvoker: ApiControllerActionInvoker
{
    public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext context, CancellationToken cancellationToken)
    {
        // Determine whether action has attribute UseStaThread
        bool useStaThread = context.ActionDescriptor.GetCustomAttributes<UseStaThreadAttribute>().Any();

        // If it doesn't, simply return the result of the base method
        if (!useStaThread)
        {
            return base.InvokeActionAsync(context, cancellationToken);
        }

        // Otherwise, create an single thread and then call the base method
        Task<HttpResponseMessage> responseTask = Task.Factory.StartNewSta(() => base.InvokeActionAsync(context, cancellationToken).Result);

        return responseTask;
    }
}

public static class TaskFactoryExtensions
{
    private static readonly TaskScheduler _staScheduler = new StaTaskScheduler(numberOfThreads: 1);

    public static Task<TResult> StartNewSta<TResult>(this TaskFactory factory, Func<TResult> action)
    {
        return factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, _staScheduler);
    }
}

public static void Register(HttpConfiguration config)
{
....
     config.Services.Replace(typeof(IHttpActionInvoker), new SmartHttpActionInvoker());
...
}

这很有效,直到我发现奇怪的事情。当未标记为单线程的方法将HttpResponseException返回给客户端时,我的日志记录数据库正在记录重复记录。当相同的方法返回OK()时,此行为不存在。

调试,我注意到代码在API方法中执行,然后到达throw语句。抛出异常以在调试器中显示的下一行是我编写的InvokeActionAsync()代码。在此之后,该方法再次运行,完全执行,命中抛出的异常,动作调用者,然后将结果返回给客户端。实际上,似乎我使用覆盖InvokeActionAsync会导致Action调用程序以某种方式被调用两次......但我不确定如何。

编辑:确认当前线程在抛出和记录时的System.Threading.Thread.CurrentThread.ManagedThreadId对于每次执行API方法都是不同的。所以,这加强了我的信念,正在创建两个线程而不是一个。仍然不确定为什么。

任何人都有覆盖可能能够解释此行为的InvokeActionAsync行为的经验吗?谢谢!

0 个答案:

没有答案