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 await
sie sich nach Abschluss wahrscheinlich in einem anderen Thread befindet, in HttpContext.Current
dem 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.Current
das 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 async
und await
neu genug ist, dass ich nichts Bestimmtes finden kann.
TL; DR: Ist HttpContext.Current
möglicherweise null
nach einem await
?
quelle
AspNetSynchronizationContext
kümmert sichHttpContext
nicht darumawait
. Darüber hinausawait
kann (und wird höchstwahrscheinlich) der Fortsetzungsrückruf für einen anderen Thread für das Web-API-Ausführungsmodell erfolgen..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 schreibtTraceInformation
.Antworten:
Stellen Sie sicher, dass Sie eine ASP.NET 4.5- Anwendung schreiben und auf 4.5 abzielen.
async
undawait
haben undefiniertes Verhalten in ASP.NET, es sei denn, Sie verwenden 4.5 und verwenden den neuen "aufgabenfreundlichen" Synchronisationskontext.Dies bedeutet insbesondere, dass Sie entweder:
httpRuntime.targetFramework
zu4.5
, oderappSettings
gesetztaspnet:UseTaskFriendlySynchronizationContext
zutrue
.Weitere Informationen finden Sie hier .
quelle
<httpRuntime targetFramework="4.5" />
ist es, was es gelöst hat, danke für die Klarstellung.targetFramework
auf 4.5.1 oder 4.5 setzen sollten / könnten , aber Async auf 4.5.1 sollte gut funktionieren.Wie @StephenCleary richtig hervorhob, benötigen Sie dies in Ihrer web.config:
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:
Doh.
Lektion: Wenn Sie ein Webprojekt auf 4.5 aktualisieren, müssen Sie diese Einstellung noch manuell vornehmen.
quelle
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.
quelle
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):
Anstelle von LegazyAspNetSynchronizationContext (was ich vor dem Hinzufügen des Zielframeworks gesehen habe):
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.
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.
quelle