我正在尝试将 AsyncController 与依赖注入混合。有问题的 MVC 应用程序通过异步 Web 服务调用获取几乎所有数据。我们将异步工作包装在 TPL 中的 Tasks 中,并在这些任务完成时通知控制器的 AsyncManager。
有时我们必须在这些任务的延续中触及 HttpContext——添加一个 cookie 等等。根据在 ASP.NET MVC 中使用异步控制器执行此操作的正确方法是调用该AsyncManager.Sync
方法。这会将 ASP.NET 线程上下文(包括 HttpContext)传播到当前线程,执行回调,然后恢复之前的上下文。
但是,那篇文章还说:
从已经受 ASP.NET 控制的线程调用 Sync() 具有未定义的行为。
如果您在控制器中完成所有工作,这不是问题,因为您通常知道在延续中应该使用哪个线程。但我想做的是在我们的异步数据访问和我们的异步控制器之间创建一个中间层。所以这些也是异步的。一切都由 DI 容器连接起来。
所以和以前一样,调用链中的一些组件需要使用“当前”HttpContext。例如,登录后,我们想要存储从单点登录服务返回的“会话”令牌。做这件事的抽象是 ISessionStore。考虑一个 CookieSessionStore,它在响应中放置一个 cookie,或者从请求中获取 cookie。
我可以看到两个问题:
- 这些组件无权访问 AsyncManager,甚至不知道它们正在控制器中使用。
- 组件不知道从哪个线程调用它们,因此 AsyncManager.Sync 或任何等效项在理论上无论如何都是有问题的。
为了解决 #1,我基本上注入了一个TaskScheduler.FromCurrentSynchronizationContext()
在请求开始时抓取的对象,并且可以通过使用该调度程序启动的任务调用一个动作,将 HttpContextBase 作为参数。
也就是说,从我的组件中,我可以调用类似于:
MySyncObject.Sync(httpContext => /* Add a cookie or something else */);
我还没有发现任何问题,但我担心问题 #2。我已经查看了ReflectorAsyncManager
和SynchronizationContextTaskScheduler
Reflector,它们的操作类似,在 ASP.NET 上执行回调SynchronizationContext
。这让我害怕 :)
当我看到任务调度程序实现将直接调用而不是通过同步上下文(如果它是内联的)时,我有点希望。但不幸的是,这似乎不是通过正常的Task.Start(scheduler)
代码路径发生的。相反,任务可以在其他情况下内联,例如在开始之前等待它们。
所以我的问题是:
- 用这种方法我会在这里遇到麻烦吗?
- 有没有更好的办法?
- 在这种情况下同步的用处仅仅是为了序列化对非线程安全的 HttpContext 的访问吗?即我可以用线程安全的 HttpContextBase 包装器代替(ick)吗?