Ответ 1
Пока ваши настройки web.config
верны, async
/await
отлично работает с HttpContext.Current
. Я рекомендую установить httpRuntime
targetFramework
на 4.5
, чтобы удалить все поведение режима "quirks".
Как только это будет сделано, обычная async
/await
будет работать отлично. У вас возникнут проблемы, если вы выполняете работу над другим потоком или если ваш код await
неверен.
Во-первых, проблема "другого потока"; это вторая проблема в блоге, с которым вы связались. Код вроде этого, конечно, не будет работать правильно:
async Task FakeAsyncMethod()
{
await Task.Run(() =>
{
var user = _userService.Current;
...
});
}
Эта проблема фактически не имеет ничего общего с асинхронным кодом; он связан с извлечением переменной контекста из потока потоков нитей (без запроса). Точно такая же проблема возникла бы, если вы попытаетесь сделать это синхронно.
Основная проблема заключается в том, что асинхронная версия использует фальшивую асинхронность. Это неуместно, особенно на ASP.NET. Решение состоит в том, чтобы просто удалить фальшиво-асинхронный код и сделать его синхронным (или действительно асинхронным, если на самом деле имеет реальную асинхронную работу):
void Method()
{
var user = _userService.Current;
...
}
Техника, рекомендуемая в связанном блоге (обертывание HttpContext
и предоставление ее рабочему потоку) чрезвычайно опасна. HttpContext
предназначен для доступа только из одного потока за раз, и AFAIK вообще не является потокобезопасным. Поэтому обмен ими между разными темами требует мира обиды.
Если код await
неверен, это вызывает аналогичную проблему. ConfigureAwait(false)
- это метод, обычно используемый в библиотечном коде для уведомления среды выполнения, что ей не нужно возвращаться в конкретный контекст. Рассмотрим этот код:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var context = HttpContext.Current;
// Note: "context" is not correct here.
// It could be null; it could be the correct context;
// it could be a context for a different request.
}
В этом случае проблема очевидна. ConfigureAwait(false)
сообщает ASP.NET, что остальной части текущего метода не нужен контекст, а затем он немедленно обращается к этому контексту. Однако, когда вы начинаете использовать значения контекста в реализациях интерфейса, проблема не такая очевидная:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var user = _userService.Current;
}
Этот код так же ошибочен, но не так явно ошибочен, поскольку контекст скрыт за интерфейсом.
Итак, общее правило: используйте ConfigureAwait(false)
, если вы знаете, что метод не зависит от его контекста (прямо или косвенно); в противном случае не используйте ConfigureAwait
. Если в вашем дизайне приемлемо реализовать реализации интерфейса, используйте контекст в их реализации, тогда любой метод, вызывающий метод интерфейса, не должен использовать ConfigureAwait(false)
:
async Task MyMethodAsync()
{
await Task.Delay(1000);
var user = _userService.Current; // works fine
}
Пока вы следуете этому руководству, async
/await
отлично работает с HttpContext.Current
.