在没有AsyncManager.Sync的情况下安全地通过ASP.NET同步上下文执行任务

Jer*_*erg 5 asynchronous task-parallel-library asp.net-mvc-3

我正在尝试将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.

我可以看到两个问题:

  1. 组件无法访问AsyncManager,甚至不知道它们是在控制器中使用的.
  2. 组件不知道从哪个线程调用它们,因此无论如何AsyncManager.Sync或任何等价物在理论上都是有问题的.

为了解决#1,我基本上TaskScheduler.FromCurrentSynchronizationContext()是在请求开始时注入一个抓取的对象,并且可以通过以该调度程序启动的Task调用一个动作,将HttpContextBase作为参数.

也就是说,从我的组件中,我可以调用类似于:

MySyncObject.Sync(httpContext => /* Add a cookie or something else */);
Run Code Online (Sandbox Code Playgroud)

我还没有发现任何问题,但我担心问题#2.我已经查看了两个AsyncManagerSynchronizationContextTaskScheduler在Reflector中,它们的操作类似,在ASP.NET上执行回调SynchronizationContext.那吓到我了:)

当我看到任务调度程序实现将直接调用而不是通过同步上下文(如果它是内联的)时,我有一点希望.但不幸的是,这似乎不是通过正常的Task.Start(scheduler)代码路径发生的.相反,任务可以在其他情况下内联,例如在他们开始之前等待他们.

所以我的问题是:

  1. 我会用这种方法遇到麻烦吗?
  2. 有没有更好的办法?
  3. 在这种情况下同步的有用性仅仅是序列化对非线程安全的HttpContext的访问吗?即我可以逃避使用线程安全的HttpContextBase包装(ick)吗?

And*_*ott 1

依赖线程本地静态很少是一个好主意。虽然HttpContext.Current依赖于这种机制并且它已经工作了很多年,但现在我们要异步了,这种方法正在迅速恶化。最好将这个静态值捕获为局部变量,并将其与异步工作一起传递,以便您始终拥有它。因此,例如:

public async Task<ActionResult> MyAction() {
    var context = HttpContext.Current;
    await Task.Yield();
    var item = context.Items["something"];
    await Task.Yield();
    return new EmptyResult();
}
Run Code Online (Sandbox Code Playgroud)

或者更好的是,HttpContext.Current如果您使用 MVC,则完全避免:

public async Task<ActionResult> MyAction() {
    await Task.Yield();
    var item = this.HttpContext.Items["something"];
    await Task.Yield();
    return new EmptyResult();
}
Run Code Online (Sandbox Code Playgroud)

可以说,您的中间件业务逻辑尤其不应该依赖于 HttpContext 或 ASP.NET 库中的其他任何内容。因此,假设您的中间件回调控制器(通过回调、接口等)来设置 cookie,那么您就可以this.HttpContext用于访问该上下文。