Mam następujący testowy kod WebAPI, nie używam WebAPI w środowisku produkcyjnym, ale zrobiłem to z powodu dyskusji, którą miałem na to pytanie: Pytanie Async WebAPI
Tak czy inaczej, oto obraźliwa metoda 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";
}
Uważałem, że drugi wyjątek jest oczekiwany, ponieważ po await
zakończeniu prawdopodobnie będzie w innym wątku, gdzie HttpContext.Current
zmienna statyczna wątku nie będzie już rozwiązywać się do odpowiedniej wartości. Teraz, w oparciu o kontekst synchronizacji, może być faktycznie zmuszony do powrotu do tego samego wątku po oczekiwaniu, ale nie robię nic nadzwyczajnego w moim teście. To tylko zwykłe, naiwne użycie await
.
W komentarzach do innego pytania powiedziano mi, że HttpContext.Current
powinno to ustąpić po odczekaniu. Jest nawet inny komentarz do tego pytania, który wskazuje na to samo. Więc co jest prawdą? Czy powinno rozwiązać? I nie myślę, ale chcę autorytatywną odpowiedź na to, bo async
i await
jest na tyle nowy, że nie mogę znaleźć nic ostateczne.
TL; DR: Czy HttpContext.Current
potencjalnie jest null
po await
?
źródło
AspNetSynchronizationContext
tym zajmujeHttpContext
, a nieawait
. Co więcej, wywołanie zwrotne kontynuacji dlaawait
może (i najprawdopodobniej wystąpi) w innym wątku dla modelu wykonywania interfejsu API sieci Web..ConfigureAwait(false)
gdzieś zaszła. Nie ma żądania ani kontrolera jawnie przekazanego przez warstwę biznesową, ponieważ ta warstwa nie obsługuje sieci. Jest to przydatne na przykład w przypadku modułu rejestrowania, który może wstrzyknąć szczegóły żądania, gdy logika biznesowa zapisuje rodzajTraceInformation
.Odpowiedzi:
Upewnij się, że piszesz aplikację ASP.NET 4,5 i jest przeznaczona dla wersji 4,5.
async
iawait
mają niezdefiniowane zachowanie w programie ASP.NET, chyba że używasz wersji 4.5 i nowego kontekstu synchronizacji „przyjaznego dla zadań”.W szczególności oznacza to, że musisz:
httpRuntime.targetFramework
na4.5
lubappSettings
, ustawaspnet:UseTaskFriendlySynchronizationContext
natrue
.Więcej informacji można znaleźć tutaj .
źródło
<httpRuntime targetFramework="4.5" />
co go rozwiązać, dzięki za wyjaśnienie.targetFramework
na 4.5.1 lub 4.5, ale asynchronizacja w 4.5.1 powinna działać dobrze.Jak poprawnie zauważył @StephenCleary, potrzebujesz tego w swoim web.config:
<httpRuntime targetFramework="4.5" />
Kiedy po raz pierwszy rozwiązywałem ten problem, przeszukałem powyższe rozwiązanie w całym rozwiązaniu, potwierdziłem, że występuje ono we wszystkich moich projektach internetowych i szybko odrzuciłem jako winowajcę. W końcu przyszło mi do głowy spojrzeć na te wyniki wyszukiwania w pełnym kontekście:
<!-- 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.
Lekcja: Jeśli zaktualizujesz projekt sieci Web do wersji 4.5, nadal musisz wprowadzić to ustawienie ręcznie.
źródło
Twój test nie jest wadliwy, a HttpContext.Current nie powinien mieć wartości null po oczekiwaniu, ponieważ w ASP.NET Web API, gdy czekasz, zapewni to, że kod następujący po tym await zostanie przekazany do prawidłowego HttpContext, który był obecny przed await.
źródło
Niedawno natknąłem się na ten problem. Jak zauważył Stephen, brak jednoznacznego określenia ram docelowych może spowodować ten problem.
W moim przypadku nasz internetowy interfejs API został zmigrowany do wersji 4.6.2, ale docelowa struktura środowiska wykonawczego nigdy nie została określona w konfiguracji internetowej, więc zasadniczo brakowało tego w tagu <system.web>:
Jeśli masz wątpliwości co do używanej wersji platformy, może to pomóc: Dodaj następujący wiersz do dowolnej metody interfejsu API sieci Web i ustaw punkt przerwania, aby sprawdzić, jaki typ jest aktualnie ładowany w czasie wykonywania i sprawdzić, czy nie jest to starsza implementacja:
Powinieneś zobaczyć to (AspNetSynchronizationContext):
Zamiast LegazyAspNetSynchronizationContext (co widziałem przed dodaniem platformy docelowej):
Jeśli przejdziesz do kodu źródłowego ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ), zobaczysz, że starszej implementacji tego interfejsu brakuje asynchronicznej obsługi.
Spędziłem dużo czasu próbując znaleźć źródło problemu, a odpowiedź Stephena bardzo pomogła. Mam nadzieję, że ta odpowiedź zawiera więcej informacji na temat problemu.
źródło