Warum ist HttpContext.Current nach dem Warten null?

87

Ich habe den folgenden Test-WebAPI-Code. Ich verwende WebAPI nicht in der Produktion, aber ich habe dies aufgrund einer Diskussion zu dieser Frage gemacht: WebAPI-Async-Frage

Wie auch immer, hier ist die beleidigende WebAPI-Methode:

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

Ich hatte bisher geglaubt, dass die zweite Ausnahme erwartet wird, da awaitsie sich nach Abschluss wahrscheinlich in einem anderen Thread befindet, in HttpContext.Currentdem sich eine statische Thread-Variable nicht mehr in den entsprechenden Wert auflöst . Basierend auf dem Synchronisationskontext könnte es nun tatsächlich gezwungen sein, nach dem Warten zum selben Thread zurückzukehren, aber ich mache in meinem Test nichts Besonderes. Dies ist nur eine einfache, naive Verwendung von await.

In Kommentaren in einer anderen Frage wurde mir gesagt, dass sich HttpContext.Currentdas nach einem Warten lösen sollte. Es gibt sogar einen weiteren Kommentar zu dieser Frage, der dasselbe anzeigt. Was ist also wahr? Sollte es sich lösen? Ich denke nein, aber ich möchte eine maßgebliche Antwort darauf, weil asyncund awaitneu genug ist, dass ich nichts Bestimmtes finden kann.

TL; DR: Ist HttpContext.Currentmöglicherweise nullnach einem await?

Welegan
quelle
3
Ihre Frage ist nicht klar - Sie haben gesagt , was Sie passieren zu erwarten, und die Kommentare zeigen , dass das ist , was passiert ... also , was Sie ist verwirrend?
Jon Skeet
@ user2674389, das ist irreführend. Es AspNetSynchronizationContextkümmert sich HttpContextnicht darum await. Darüber hinaus awaitkann (und wird höchstwahrscheinlich) der Fortsetzungsrückruf für einen anderen Thread für das Web-API-Ausführungsmodell erfolgen.
Noseratio
bearbeitet, um eine prägnante Frage zu stellen
welegan
1
@JoepBeusenberg Das Erstellen separater Assemblys, die nur funktionieren, wenn sie von einer Assembly aufgerufen werden, die im Kontext einer HTTP-Anforderung eines bestimmten Webstacks ausgeführt wird, scheint das Testen, Warten und Wiederverwenden zu einer Herausforderung zu machen.
Darrel Miller
1
@DarrelMiller Ganz im Gegenteil. Ich habe die Geschäftslogik vom eigentlichen Webprojekt getrennt. Mithilfe der Abhängigkeitsinjektion kann ich zusätzlich zur Geschäftslogik eine webapi-fähige Bibliothek hinzufügen. Diese Bibliothek bricht jedoch zusammen, wenn die Geschäftslogik .ConfigureAwait(false)irgendwo auf der ganzen Linie funktioniert hat . Es gibt keine Anfrage oder Steuerung, die explizit über die Geschäftsschicht weitergeleitet wird, da diese nicht webfähig ist. Dies ist beispielsweise für ein Protokollierungsmodul nützlich, das die Anforderungsdetails einfügen kann, wenn die Geschäftslogik ein Generikum schreibt TraceInformation.
Joep Beusenberg

Antworten:

146

Stellen Sie sicher, dass Sie eine ASP.NET 4.5- Anwendung schreiben und auf 4.5 abzielen. asyncund awaithaben undefiniertes Verhalten in ASP.NET, es sei denn, Sie verwenden 4.5 und verwenden den neuen "aufgabenfreundlichen" Synchronisationskontext.

Dies bedeutet insbesondere, dass Sie entweder:

  • Stellen Sie httpRuntime.targetFrameworkzu 4.5, oder
  • In Ihrem appSettingsgesetzt aspnet:UseTaskFriendlySynchronizationContextzu true.

Weitere Informationen finden Sie hier .

Stephen Cleary
quelle
2
Ich habe gerade ein neues ASP.NET 4.5-WebAPI-Projekt erstellt, Ihren Code kopiert / eingefügt und einen Test durchgeführt. Es hat perfekt für mich funktioniert (keine Ausnahme wurde geworfen). Bitte überprüfen Sie erneut, ob Sie auf 4.5 ausgeführt werden und auf 4.5 abzielen .
Stephen Cleary
3
Ich habe Ziel-Framework: .NET Framework 4.5 festgelegt. Ich weiß nicht, was ich Ihnen sagen soll, auf meinem lokalen Computer ist es definitiv null.
Welegan
24
Das <httpRuntime targetFramework="4.5" />ist es, was es gelöst hat, danke für die Klarstellung.
Welegan
1
@Vince: 4.5.1 sollte gut funktionieren. Ich bin nicht sicher, ob Sie targetFrameworkauf 4.5.1 oder 4.5 setzen sollten / könnten , aber Async auf 4.5.1 sollte gut funktionieren.
Stephen Cleary
1
Was ist, wenn Sie Ihren eigenen verwalteten Handler geschrieben haben? Ich denke immer wieder an HttpContext.Current = null, selbst nachdem ich diese Elemente in die web.config eingefügt habe.
Brain2000
26

Wie @StephenCleary richtig hervorhob, benötigen Sie dies in Ihrer web.config:

<httpRuntime targetFramework="4.5" />

Als ich dies zum ersten Mal behoben habe, habe ich eine lösungsweite Suche nach dem oben genannten durchgeführt, bestätigt, dass es in allen meinen Webprojekten vorhanden ist, und es schnell als Schuldigen abgetan. Schließlich kam mir der Gedanke, diese Suchergebnisse im vollständigen Kontext zu betrachten:

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

Lektion: Wenn Sie ein Webprojekt auf 4.5 aktualisieren, müssen Sie diese Einstellung noch manuell vornehmen.

Todd Menier
quelle
21
Ein weiteres Problem ist, dass dies anders ist als <compilation targetFramework "4.5" />
Andrew
3

Ist mein Test fehlerhaft oder fehlt hier ein web.config-Element, das dazu führen würde, dass HttpContext.Current nach einer Wartezeit korrekt aufgelöst wird?

Ihr Test ist nicht fehlerhaft und HttpContext.Current sollte nach dem Warten nicht null sein, da in der ASP.NET-Web-API beim Warten sichergestellt wird, dass dem Code, der auf dieses Warten folgt, der richtige HttpContext übergeben wird, der vor dem Warten vorhanden war.

Darin Dimitrov
quelle
Sind Sie sich über denselben Fortsetzungsthread für WebAPI sicher? Ich habe mich mit dem Fall befasst, in dem es sich um einen anderen Thread handelte.
Noseratio
4
ASP.NET wird in jedem Thread-Pool-Thread fortgesetzt, jedoch mit dem richtigen Anforderungskontext.
Stephen Cleary
2
Ja, Sie haben Recht, der Thread ist möglicherweise nicht derselbe, aber HttpContext.Current ist derselbe wie vor dem Warten. Ich habe meine Frage aktualisiert.
Darin Dimitrov
3
HttpContext.Current ist nach einem Warten in meinem Code null und ich ziele auf .net 4.6.1.
Triynko
1
für mich ist HttpContext.Current null vor einer
Wartefunktion
1

Ich bin kürzlich auf dieses Problem gestoßen. Wie Stephen betonte, kann das Ziel-Framework dieses Problem erzeugen, wenn es nicht explizit festgelegt wird.

In meinem Fall wurde unsere Web-API auf Version 4.6.2 migriert, aber das Laufzeitziel-Framework wurde in der Webkonfiguration nie angegeben, sodass dies im Tag <system.web> im Grunde fehlte:

Wenn Sie Zweifel an der von Ihnen ausgeführten Framework-Version haben, kann dies hilfreich sein: Fügen Sie die folgende Zeile zu einer Ihrer Web-API-Methoden hinzu und legen Sie einen Haltepunkt fest, um zu überprüfen, welcher Typ derzeit zur Laufzeit geladen wird, und um sicherzustellen, dass es sich nicht um eine Legacy-Implementierung handelt:

Sie sollten dies sehen (AspNetSynchronizationContext):

Geben Sie hier die Bildbeschreibung ein

Anstelle von LegazyAspNetSynchronizationContext (was ich vor dem Hinzufügen des Zielframeworks gesehen habe):

Geben Sie hier die Bildbeschreibung ein

Wenn Sie zum Quellcode ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ) wechseln, werden Sie feststellen, dass die Legacy-Implementierung dieser Schnittstelle keine asynchrone Unterstützung bietet.

Geben Sie hier die Bildbeschreibung ein

Ich habe viel Zeit damit verbracht, die Ursache des Problems zu finden, und Stephens Antwort hat mir sehr geholfen. Ich hoffe, diese Antwort enthält weitere Informationen zu diesem Problem.

abarrenechea
quelle