Почему HttpContext.Current имеет значение null после ожидания?


91

У меня есть следующий тестовый код WebAPI, я не использую WebAPI в производстве, но я сделал это из-за обсуждения, которое у меня было по этому вопросу: Вопрос об асинхронности WebAPI

В любом случае, вот оскорбительный метод 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должно разрешиться после ожидания. Есть даже еще один комментарий по этому вопросу, указывающий на то же самое. Так что правда? Должен ли он разрешиться? Думаю , что нет, но я хочу , авторитетный ответ на это , потому что asyncи awaitэто достаточно новый , что я не могу найти ничего окончательного.

TL; DR: HttpContext.Currentпотенциально nullпосле await?


3
Ваш вопрос не ясно , - вы сказали , что должно было произойти, и комментарии свидетельствуют о том , что это то , что происходит ... так что вас смущает?
Джон Скит,

@ user2674389, это вводит в заблуждение. Это то, AspNetSynchronizationContextо чем заботятся HttpContext, а не await. Более того, обратный вызов продолжения для awaitможет (и, скорее всего, будет) происходить в другом потоке для модели выполнения веб-API.
Nosratio

отредактировал, чтобы задать краткий вопрос
welegan

1
@JoepBeusenberg Создание отдельных сборок, которые работают только тогда, когда они вызываются из сборки, которая выполняется в контексте HTTP-запроса одного конкретного веб-стека, похоже, может затруднить тестирование, обслуживание и повторное использование.
Даррел Миллер

1
@DarrelMiller Скорее наоборот. Я отделил бизнес-логику от реального веб-проекта. Используя внедрение зависимостей, я могу добавить библиотеку с поддержкой webapi поверх бизнес-логики. Но эта библиотека перестает работать, когда бизнес-логика .ConfigureAwait(false)где-то работает. Нет никакого запроса или контроллера, явно передаваемого через бизнес-уровень, поскольку он не поддерживает веб. Это полезно, например, для модуля ведения журнала, который может вводить детали запроса, когда бизнес-логика записывает общий TraceInformation.
Joep Beusenberg

Ответы:


150

Убедитесь, что вы пишете приложение ASP.NET 4.5 и ориентируетесь на 4.5. asyncи awaitимеют неопределенное поведение в ASP.NET, если вы не работаете в 4.5 и не используете новый «удобный для задач» контекст синхронизации.

В частности, это означает, что вы должны:

  • Установите httpRuntime.targetFrameworkна 4.5, или
  • В вашем appSettingsустановите aspnet:UseTaskFriendlySynchronizationContextзначение true.

Более подробная информация доступна здесь .


2
Я только что создал новый проект ASP.NET 4.5 WebAPI, скопировал / вставил ваш код и провел тест. У меня это сработало отлично (ни одно исключение не было создано). Пожалуйста, еще раз убедитесь, что вы используете версию 4.5.
Стивен Клири

3
У меня установлен Target framework: .NET Framework 4.5. Я не знаю, что вам сказать, на моей локальной машине это определенно ноль.
welegan

24
<httpRuntime targetFramework="4.5" />что решается, спасибо за разъяснение.
welegan

1
@Vince: 4.5.1 должно работать нормально. Я не уверен, следует ли вам / можно установить targetFramework4.5.1 или 4.5, но async на 4.5.1 должен работать нормально.
Стивен Клири

1
Что, если бы вы написали свой собственный управляемый обработчик? Я продолжаю придумывать HttpContext.Current = null даже после добавления этих элементов в web.config.
Brain2000

30

Как правильно указал @StephenCleary, вам нужно это в вашем web.config:

<httpRuntime targetFramework="4.5" />

Когда я впервые занимался устранением неполадок, я выполнил поиск вышеуказанного решения по всему решению, подтвердил, что он присутствует во всех моих веб-проектах, и быстро отклонил его как виновника. В конце концов мне пришло в голову взглянуть на эти результаты поиска в полном контексте:

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

Дох.

Урок: если вы обновляете веб-проект до версии 4.5, вам все равно нужно установить этот параметр вручную.


22
Еще одна проблема заключается в том, что это отличается от <compilation targetFramework "4.5" />
Эндрю

3

Мой тест ошибочен, или мне здесь не хватает какого-то элемента web.config, который заставил бы HttpContext.Current разрешиться правильно после ожидания?

Ваш тест не содержит ошибок, и HttpContext.Current не должен иметь значение null после ожидания, потому что в веб-API ASP.NET, когда вы ожидаете, это гарантирует, что код, следующий за этим ожиданием, будет передан правильному HttpContext, который присутствовал до ожидания.


Вы уверены, что продолжение темы WebAPI будет таким же? Я имел дело со случаем, когда это была другая ветка.
Nosratio

4
ASP.NET возобновит работу в любом потоке пула потоков, но с правильным контекстом запроса.
Стивен Клири

2
Да, вы правы, поток может быть не таким, но HttpContext.Current будет таким же, как и до ожидания. Я обновил свой вопрос.
Дарин Димитров

4
HttpContext.Current имеет значение null после ожидания в моем коде, и я нацелен на .net 4.6.1.
Трийнко

1
для меня HttpContext.Current имеет значение null перед функцией ожидания
JobaDiniz

3

Я недавно столкнулся с этой проблемой. Как отметил Стивен, отсутствие явной настройки целевой структуры может вызвать эту проблему.

В моем случае наш веб-API был перенесен в версию 4.6.2, но целевая среда выполнения никогда не была указана в веб-конфигурации, поэтому в основном это отсутствовало внутри тега <system.web>:

Если у вас есть сомнения по поводу используемой вами версии фреймворка, это может помочь: добавьте следующую строку в любой из ваших методов веб-API и установите точку останова, чтобы проверить, какой тип в настоящее время загружается во время выполнения, и убедиться, что это не реализация Legacy:

Вы должны увидеть это (AspNetSynchronizationContext):

введите описание изображения здесь

Вместо LegazyAspNetSynchronizationContext (что я видел перед добавлением целевой платформы):

введите описание изображения здесь

Если вы перейдете к исходному коду ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ), вы увидите, что в устаревшей реализации этого интерфейса отсутствует поддержка асинхронности.

введите описание изображения здесь

Я потратил много времени, пытаясь найти источник проблемы, и ответ Стивена мне очень помог. Надеюсь, этот ответ предоставит дополнительную информацию о проблеме.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.