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

时间:2013-08-22 14:53:26

标签: c# asp.net asp.net-web-api async-await

我有以下测试WebAPI代码,我没有在生产中使用WebAPI,但是我之所以这样做是因为我对这个问题的讨论:WebAPI Async question

无论如何,这是违规的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之后null可能会await吗?

5 个答案:

答案 0 :(得分:125)

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

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

  • httpRuntime.targetFramework设为4.5
  • appSettings中,将aspnet:UseTaskFriendlySynchronizationContext设为true

更多信息是available here

答案 1 :(得分:21)

正如@StephenCleary正确指出的那样,你需要在web.config中使用它:

<httpRuntime targetFramework="4.5" />

当我第一次对此进行故障排除时,我在上面进行了解决方案范围内的搜索,确认它存在于我的所有网络项目中并很快将其视为罪魁祸首。最终我发现在完整的背景下查看这些搜索结果:

<!--
  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>
-->

卫生署。

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

答案 2 :(得分:3)

  

我的测试是否存在缺陷,或者是否存在一些我缺少的web.config元素   这将使HttpContext.Current在一个之后正确解析   等待?

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

答案 3 :(得分:2)

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

在我的情况下,我们的Web API迁移到了4.6.2版,但从未在Web配置中指定运行时目标框架,因此,基本上标记中没有此内容:

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

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

enter image description here

代替LegazyAspNetSynchronizationContext(添加目标框架之前我所看到的是这样):

enter image description here

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

enter image description here

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

答案 4 :(得分:0)

我想发表评论并说,前面的答案从 web.config 的角度,但是,可以从 IIS 继承设置,这些设置可以覆盖此功能,如果您只是使用 <httpRuntime targetFramework="4.5" />

我的意思: 这里真正的修复是这个设置: <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

如果您没有明确包含此设置,而是依赖 <httpRuntime targetFramework="4.5" /> 来配置此设置 - 它将被 IIS 中的任何设置覆盖。

如果您正在调试或记录 SynchronizationContext 的类型并发现它是 Legacy 类型,您可能需要检查 IIS 或托管站点级别的设置。

将产生 LegacyAspNetSynchronizationContext:

Web.config:

    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="aspnet:UseLegacyEncryption" value="true" />
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.8" />
    <httpRuntime targetFramework="4.5" />
  </system.web>

enter image description here

将产生 AspNetSynchronizationContext(这是您想要的):

    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
    <add key="aspnet:UseLegacyEncryption" value="true" />
    <add key="aspnet:UseLegacyMachineKeyEncryption" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.8" />
    <httpRuntime targetFramework="4.5" />
  </system.web>

*注意这里的 IIS 中没有覆盖设置