我最近遇到了一个问题,我正在开发一个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行为的经验吗?谢谢!