加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc-3 – 安全地通过ASP.NET同步上下文执行任务,而不使

发布时间:2020-12-16 07:45:29 所属栏目:asp.Net 来源:网络整理
导读:我正在尝试将AsyncController与依赖注入混合使用.有问题的MVC应用程序通过异步Web服务调用获取几乎所有数据.我们将TPL中的异步工作包装在Tasks中,并在完成这些任务时通知控制器的AsyncManager. 有时我们必须在这些任务的延续中触摸HttpContext – 添加cookie
我正在尝试将AsyncController与依赖注入混合使用.有问题的MVC应用程序通过异步Web服务调用获取几乎所有数据.我们将TPL中的异步工作包装在Tasks中,并在完成这些任务时通知控制器的AsyncManager.

有时我们必须在这些任务的延续中触摸HttpContext – 添加cookie,无论如何.根据Using an Asynchronous Controller in ASP.NET MVC执行此操作的正确方法是调用AsyncManager.Sync方法.这会将ASP.NET线程上下文(包括HttpContext)传播到当前线程,执行回调,然后恢复先前的上下文.

但是,该文章还说:

Calling Sync() from a thread that is already under the control of ASP.NET has undefined behavior.

如果你在控制器中完成所有工作,这不是问题,因为你通常知道你应该在延续中使用什么线程.但我要做的是在我们的异步数据访问和异步控制器之间创建一个中间层.所以这些也是异步的.所有东西都通过DI容器连接起来.

和以前一样,调用链中的一些组件需要使用“当前”的HttpContext.例如,在登录后我们想要存储“会话”令牌,我们从单点登录服务返回.这样做的东西的抽象是ISessionStore.想象一下CookieSessionStore会在响应中放置cookie,或者从请求中获取cookie.

我可以看到两个问题:

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

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

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

MySyncObject.Sync(httpContext => /* Add a cookie or something else */);

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

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

所以我的问题是:

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

解决方法

依赖线程局部静态很少是一个好主意.虽然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();
}

或者甚至更好,如果你在MVC中,完全避免使用HttpContext.Current:

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

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读