97 votes

Pourquoi HttpContext.Current est null après await ?

J'ai le code WebAPI de test suivant, je n'utilise pas WebAPI en production mais je l'ai fait à cause d'une discussion que j'ai eue sur cette question : Question sur l'interface WebAPI asynchrone

Quoi qu'il en soit, voici la méthode WebAPI incriminée :

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";
}

J'avais cru jusqu'ici que la deuxième exception était attendue parce que, lorsque les await complète, ce sera probablement sur un autre fil de discussion où HttpContext.Current en tant que variable statique ne sera plus résolu à la valeur appropriée. Maintenant, sur la base du contexte de synchronisation, il pourrait être forcé de revenir au même thread après l'attente, mais je ne fais rien de compliqué dans mon test. C'est juste une simple utilisation naïve de await .

Dans les commentaires d'une autre question, on m'a dit que HttpContext.Current doit être résolu après une attente. Il y a même un autre commentaire sur cette question qui indique la même chose. Alors, qu'est-ce qui est vrai ? Doit-il être résolu ? Je pense que non, mais je veux une réponse faisant autorité sur ce point car async y await est assez récente pour que je ne puisse pas trouver quelque chose de définitif.

TL;DR : Est HttpContext.Current potentiellement null après un await ?

159voto

Stephen Cleary Points 91731

Veuillez vous assurer que vous écrivez un programme ASP.NET 4.5 et le ciblage 4.5. async y await ont un comportement non défini sur ASP.NET, sauf si vous utilisez la version 4.5. y utilisent le nouveau contexte de synchronisation "convivial".

En particulier, cela signifie que vous devez soit :

  • Définir httpRuntime.targetFramework à 4.5 ou
  • Dans votre appSettings ensemble aspnet:UseTaskFriendlySynchronizationContext à true .

Pour plus d'informations, voir disponible ici .

32voto

Todd Menier Points 3599

Comme @StephenCleary l'a correctement souligné, vous avez besoin de cela dans votre web.config :

<httpRuntime targetFramework="4.5" />

Lorsque j'ai commencé à résoudre ce problème, j'ai effectué une recherche sur l'ensemble de la solution, j'ai confirmé qu'elle était présente dans tous mes projets Web et je l'ai rapidement rejetée comme coupable. Finalement, j'ai eu l'idée d'examiner les résultats de la recherche dans leur contexte :

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

Doh.

Leçon : si vous mettez à niveau un projet Web vers 4.5, vous devez toujours mettre en place ce paramètre manuellement.

7voto

abarrenechea Points 64

J'ai rencontré ce problème récemment. Comme Stephen l'a souligné, le fait de ne pas définir explicitement le cadre cible peut générer ce problème.

Dans mon cas, notre API Web a été migrée vers la version 4.6.2, mais le cadre d'exécution cible n'a jamais été spécifié dans la configuration Web, ce qui signifie qu'il manquait dans la balise <system.web> :

Si vous avez des doutes sur la version du framework que vous utilisez, ceci peut vous aider : Ajoutez la ligne suivante à n'importe laquelle de vos méthodes d'API Web et définissez un point d'arrêt pour vérifier quel type est actuellement chargé au moment de l'exécution et vérifier qu'il ne s'agit pas d'une implémentation héritée :

Vous devriez voir ceci (AspNetSynchronizationContext) :

enter image description here

Au lieu de LegazyAspNetSynchronizationContext (qui était ce que je voyais avant d'ajouter le cadre cible) :

enter image description here

Si vous allez dans le code source ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ), vous verrez que l'implémentation Legacy de cette interface manque de support asynchrone.

enter image description here

J'ai passé beaucoup de temps à essayer de trouver la source du problème et la réponse de Stephen m'a beaucoup aidé. J'espère que cette réponse fournira davantage d'informations sur le problème.

3voto

Darin Dimitrov Points 528142

Est-ce que mon test est défectueux, ou est-ce qu'il y a un élément web.config que je manque qui ferait en sorte que HttpContext.Current soit résolu correctement après un await ?

Votre test n'est pas défectueux et HttpContext.Current ne devrait pas être nul après le await car dans ASP.NET Web API, lorsque vous awaitez, cela garantit que le code qui suit ce await reçoit le HttpContext correct qui était présent avant le await.

1voto

Mark C. Points 4647

Je voulais commenter et dire que les réponses précédentes ont fait mouche. du point de vue du web.config Cependant, il peut y avoir des paramètres hérités d'IIS qui peuvent remplacer cette fonctionnalité si vous utilisez simplement l'option <httpRuntime targetFramework="4.5" /> .

Ce que je veux dire : La vraie solution ici est ce paramètre : <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

Si vous n'incluez pas explicitement ce paramètre mais comptez sur <httpRuntime targetFramework="4.5" /> pour configurer ce paramètre - il sera remplacé par tous les paramètres de IIS.

Si vous déboguez ou enregistrez le type de SynchronizationContext et que vous découvrez qu'il est de type Legacy, vous pouvez vérifier si un paramètre est présent au niveau de IIS ou du site d'hébergement.

Donne lieu à 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

Produira AspNetSynchronizationContext (c'est ce que vous voulez) :

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

*Notez qu'il n'y a pas de paramètre prioritaire dans IIS ici.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X