为什么等待后HttpContext.Current为null?


89

我有以下测试WebAPI代码,我不在生产中使用WebAPI,但由于进行了有关此问题的讨论,所以进行了此操作:WebAPI异步问题

无论如何,这是令人讨厌的WebAPI方法:

public async Task<string> Get(int id)
{
    var x = HttpContext.Current;
    if (x == null)
    {
        // not thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    await Task.Run(() => { Task.Delay(500); id = 3; });

    x = HttpContext.Current;
    if (x == null)
    {
        // thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    return "value";
}

至此,我相信第二个异常是可以预期的,因为当await完成时,第二个异常可能会在另一个线程上,在那里HttpContext.Current,线程静态变量将不再解析为适当的值。现在,基于同步上下文,实际上它可能被迫在等待后返回到同一线程,但是我在测试中没有做任何花哨的事情。这只是的简单,幼稚的用法await

在另一个问题的评论中,我被告知HttpContext.Current应该在等待后解决。关于这个问题,甚至还有另一条评论表明了这一点。那是真的吗?它应该解决吗?我认为没有,但是我想要一个权威的答案,因为asyncawait足够新,所以我找不到任何确定的答案。

TL; DR:HttpContext.Current可能nullawait?之后吗?


3
您的问题尚不清楚-您已经说了预期会发生什么,而评论则表明发生了什么……那什么让您感到困惑?
乔恩·斯基特

@ user2674389,这具有误导性。这AspNetSynchronizationContext是照顾HttpContext,而不是await。此外,await针对Web API执行模型的继续回调可能(而且很可能会)发生在不同的线程上。
noseratio

编辑提出了一个简洁的问题
welegan

1
@JoepBeusenberg创建单独的程序集,这些程序集仅在从一个特定Web堆栈的HTTP请求的上下文中执行的程序集中调用时才起作用,这似乎会使测试,维护和重用成为挑战。
Darrel Miller

1
@DarrelMiller恰恰相反。我已经将业务逻辑与实际的Web项目分离了。使用依赖注入,我可以在业务逻辑之上添加一个支持webapi的库。但是,当业务逻辑在.ConfigureAwait(false)某处完成时,该库就会中断。没有请求或控制器显式传递到业务层,因为该请求或控制器不支持Web。例如,这对于日志记录模块很有用,该模块可以在业务逻辑编写泛型时注入请求详细信息TraceInformation
Joep Beusenberg

Answers:


148

请确保您正在编写ASP.NET 4.5应用程序并以4.5为目标。asyncawait在ASP.NET上具有未定义的行为,除非您在4.5上运行并且正在使用新的“任务友好”同步上下文。

特别是,这意味着您必须:

  • 设置httpRuntime.targetFramework4.5,或
  • 在中appSettings,设置aspnet:UseTaskFriendlySynchronizationContexttrue

可在此处获得更多信息。


2
我刚刚创建了一个新的ASP.NET 4.5 WebAPI项目,复制/粘贴了您的代码,并进行了测试。它对我来说非常有效(都未抛出异常)。请重新检查您是否正在运行并定位到4.5。
斯蒂芬·克利西

3
我有目标框架:.NET Framework 4.5集。我不知道该告诉您什么,在我的本地计算机上肯定是null。
welegan13年

24
<httpRuntime targetFramework="4.5" />是什么样解决它,感谢澄清。
welegan

1
@Vince:4.5.1应该可以正常工作。我不确定是否应该/可以将其设置targetFramework为4.5.1或4.5,但是4.5.1上的异步应该可以正常工作。
Stephen Cleary

1
如果您编写了自己的托管处理程序怎么办?即使在web.config中添加了这些项目之后,我仍然在那里提出HttpContext.Current = null。
Brain2000

28

正如@StephenCleary正确指出的那样,您需要在web.config中:

<httpRuntime targetFramework="4.5" />

当我第一次对此进行故障排除时,我对上述内容进行了全解决方案搜索,确认其存在于我的所有Web项目中,并迅速将其视为罪魁祸首。最终,我想到了要全面了解这些搜索结果:

<!--
  For a description of web.config changes for .NET 4.5 see http://go.microsoft.com/fwlink/?LinkId=235367.

  The following attributes can be set on the <httpRuntime> tag.
    <system.Web>
      <httpRuntime targetFramework="4.5" />
    </system.Web>
-->

h

课程:如果将Web项目升级到4.5,则仍需要手动进行该设置。


22
另一个陷阱是,这不同于<compilation targetFramework“ 4.5” />
Andrew

3

我的测试是否有缺陷,还是我缺少一些web.config元素,它会使HttpContext.Current在等待后正确解析?

您的测试没有缺陷,并且等待之后HttpContext.Current不应为null,因为在ASP.NET Web API中,当您等待时,这将确保此等待之后的代码传递的是在等待之前存在的正确HttpContext。


您确定WebAPI具有相同的延续线程吗?我处理的情况下,它是一个不同的线程。
noseratio

4
ASP.NET将在任何线程池线程上恢复,但是具有正确的请求上下文。
Stephen Cleary

2
是的,您是对的,线程可能不同,但是HttpContext.Current与等待之前的线程相同。我已经更新了我的问题。
Darin Dimitrov

4
在我的代码中等待之后,HttpContext.Current为null,并且我的目标是.net 4.6.1。
Triynko

1
对我来说HttpContext.Current等待函数之前为null
JobaDiniz

2

我最近遇到了这个问题。正如斯蒂芬指出的那样,未明确设置目标框架会导致此问题。

在我的情况下,我们的Web API迁移到了4.6.2版,但是运行时目标框架从未在Web配置中指定,因此,基本上<system.web>标记中缺少此框架:

如果您对正在运行的框架版本有疑问,这可能会有所帮助:在您的任何Web API方法上添加以下行,并设置一个断点以验证运行时当前加载的类型,并确认它不是旧版实现:

您应该看到以下内容(AspNetSynchronizationContext):

在此处输入图片说明

而不是LegazyAspNetSynchronizationContext(添加目标框架之前我所看到的是):

在此处输入图片说明

如果转到源代码(https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs),您将看到此接口的旧版实现缺少异步支持。

在此处输入图片说明

我花了很多时间试图找到问题的根源,斯蒂芬的回应也帮了很多忙。希望此答案提供有关此问题的更多信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.