Чому HttpContext.Current дорівнює нулю після await?


89

У мене є такий тестовий код WebAPI, я не використовую WebAPI у виробництві, але я зробив це через обговорення цього питання: WebAPI Async question

У будь-якому випадку, ось образливий метод 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.
noseratio

відредаговано для
короткого

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

1
@DarrelMiller Зовсім навпаки. Я відокремив бізнес-логіку від власне веб-проекту. Використовуючи ін’єкцію залежностей, я можу додати бібліотеку з інформацією про webapi поверх бізнес-логіки. Але ця бібліотека руйнується, коли ділова логіка зробила .ConfigureAwait(false)десь нижчий рівень. Немає жодного запиту або контролера, явно переданого через бізнес-рівень, оскільки цей не знає Інтернет. Це корисно, наприклад, для модуля реєстрації, який може вводити деталі запиту, коли бізнес-логіка пише загальний TraceInformation.
Джоеп Беузенберг,

Відповіді:


148

Переконайтесь, що ви пишете програму 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 set. Не знаю, що вам сказати, на моїй локальній машині це точно нуль.
welegan

24
саме <httpRuntime targetFramework="4.5" />це вирішило, дякую за роз’яснення.
welegan

1
@Vince: 4.5.1 повинен працювати нормально. Я не впевнений, чи слід / можна встановити targetFramework4.5.1 або 4.5, але асинхронізація 4.5.1 повинна працювати нормально.
Стівен Клірі

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

28

Як правильно зазначив @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 не повинен бути нульовим після await, оскільки в веб-API ASP.NET, коли ви чекаєте, це забезпечить передачу коду, який слідує за цим await, правильним HttpContext, який був до await.


Ви впевнені в тому ж потоці продовження для WebAPI? Я мав справу зі справою, коли це була інша нитка.
noseratio

4
ASP.NET відновиться до будь-якого потоку пулу потоків, але з правильним контекстом запиту.
Стівен Клірі

2
Так, ви маєте рацію, потік може бути не однаковим, але HttpContext.Current буде таким же, як і до await. Я оновив своє запитання.
Дарін Димитров

4
HttpContext.Current має значення null після очікування в моєму коді, і я націлююся на .net 4.6.1.
Трійко

1
для мене HttpContext.Current є нульовим перед функцією await
JobaDiniz

2

Нещодавно я зіткнувся з цим питанням. Як зазначав Стівен, якщо чітко не встановити цільові рамки, це може породити цю проблему.

У моєму випадку наш веб-API було перенесено на версію 4.6.2, але цільова структура середовища виконання ніколи не була вказана у веб-конфігурі, тому в основному цього не було в тезі <system.web>:

Якщо у вас є сумніви щодо запущеної версії фреймворку, це може допомогти: Додайте наступний рядок до будь-якого з ваших методів веб-API та встановіть точку зупинки, щоб перевірити, який тип завантажується в даний час під час виконання та переконатися, що це не застаріла реалізація:

Ви повинні побачити це (AspNetSynchronizationContext):

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

Замість LegazyAspNetSynchronizationContext (що було те, що я бачив до того, як додати цільову структуру):

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

Якщо ви перейдете до вихідного коду ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ), ви побачите, що у застарілій реалізації цього інтерфейсу відсутня асинхронна підтримка.

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

Я витратив багато часу, намагаючись знайти джерело проблеми, і відповідь Стівена дуже допомогла. Сподіваюся, що ця відповідь надає додаткову інформацію про проблему.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.