97

我有以下测试 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可能是nullawait?

4

6 回答 6

159

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

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

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

更多信息可在此处获得

于 2013-08-22T15:06:15.823 回答
32

正如@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>
-->

多哈。

教训:如果您将 Web 项目升级到 4.5,您仍然需要手动进行该设置。

于 2014-01-28T18:16:31.043 回答
6

我最近遇到了这个问题。正如斯蒂芬指出的那样,没有明确设置目标框架会产生这个问题。

在我的例子中,我们的 Web API 已迁移到版本 4.6.2,但运行时目标框架从未在 Web 配置中指定,所以基本上 <system.web> 标记中缺少了这一点:

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

你应该看到这个(AspNetSynchronizationContext):

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

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

于 2020-07-23T19:54:22.847 回答
3

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

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

于 2013-08-22T14:58:07.057 回答
1

我想评论说,从 web.config 的角度来看,前面的答案一针见血,但是,如果您简单地使用<httpRuntime targetFramework="4.5" />.

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

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

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

将产生 LegacyAspNetSynchronizationContext:

网络配置:

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

在此处输入图像描述

将产生 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 中没有覆盖设置

于 2021-04-13T20:44:11.507 回答
0

我在这里尝试了所有其他答案,HttpContext.Current但仍然为空。

.net 框架 4.6.1 / MVC

HttpContext.Current用来获取上传到的映射路径App_Data

这就是我解决问题的方法:

我只是获取HttpContext.Current变量中的电流,然后在await调用后将其重置。

var tempHttpContextCurrent = System.Web.HttpContext.Current;

var report = await reportingUtilities.GetReport(reportId, currentUserRubixId).ConfigureAwait(false);

// reset the HttpContext.Current after the await call.
System.Web.HttpContext.Current = tempHttpContextCurrent;
于 2021-09-11T17:14:30.343 回答