HttpContext.Current.Items после операции Async
Рассмотрим следующий обработчик делегирования веб-API ASP.NET:
public class MyHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var guid = Guid.NewGuid();
HttpContext.Current.Items["foo"] = guid;
// An Async operation
var result = await base.SendAsync(request, cancellationToken);
//All code from this point is not gauranteed to run on the same thread that started the handler
var restoredGuid = (Guid)HttpContext.Current.Items["foo"];
//Is this gauranteed to be true
var areTheSame = guid == restoredGuid;
return result;
}
}
Вышеприведенный пример относится к обработчику делегирования, та же проблема, которую я пытаюсь исправить, применяется в контроллерах, бизнес-объектах и т.д.
В конечном итоге я пытаюсь предоставить некоторое простое совместное состояние в памяти между различными объектами в HTTP-запросе
Как я понимаю, во время операций Async поток ASP.NET, первоначально запускающий операцию, возвращается в пул потоков, и для завершения запроса после завершения операции Async может использоваться другой поток.
Это влияет на коллекцию HttpContext.Current.Items
?
Является ли элемент, который был в коллекции Items
гарантированно присутствует, когда Запрос возобновляется?
-
Я знаю, что использование HttpContext.Current
часто неодобрительно
более широкое сообщество в наши дни по причинам, которые я полностью согласен
с... Я просто помогаю кому-то из пробки.
-
Сохранение этих данных в коллекции Request.Items
не подходит для решения этой проблемы, так как мой коллега требует статичности из-за некоторых плохих проектных решений.
Большое спасибо
Ответы
Ответ 1
Как я понимаю, во время операций Async поток ASP.NET, первоначально запускающий операцию, возвращается в пул потоков, и для завершения запроса после завершения операции Async может использоваться другой поток.
Это правильно. Но позвольте говорить о async
на ASP.NET на минуту.
async
требуется .NET 4.5. Кроме того, ASP.NET 4.5 вводит режим "quirks" на стороне сервера, и вам нужно отключить quirk SynchronizationContext
. Вы можете сделать это, установив httpRuntime.targetFramework
в 4.5
или используя appSettings
с aspnet:UseTaskFriendlySynchronizationContext
значением true
.
Если ваш web.config не имеет одной из этих записей, то поведение async
равно undefined. Подробнее см. этот пост. Я рекомендую использовать параметр targetFramework
и исправлять любые возникающие проблемы.
Это влияет на коллекцию HttpContext.Current.Items? Является ли элемент, который был в коллекции Items гарантированно присутствовать при возобновлении запроса?
AspNetSynchronizationContext
сохраняет текущий контекст запроса в точках await
. Это включает в себя HttpContext.Current
(который включает в себя Items
, User
и т.д.).
Другая возможность - CallContext.Logical[Get|Set]Data
, которая также проходит через точки await
. Это полезно, если вам не нужна зависимость кода от HttpContext
, но имеет немного больше накладных расходов.
Я поговорил на этой конференции пару недель назад на async
на стороне сервера; вы можете найти слайды полезными, особенно те, которые связаны с контекстом и потоком-локальным состоянием.
Ответ 2
Сокращение длинной истории коротко, это нормально. Если вы не используете ConfigureAwait(false)
, который может иметь побочный эффект с продолжением, не текущим контекстом.
Попробуйте добавить этот параметр в свое приложение.
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
UPDATE
Примечание!!
Первоначально я ставлю ложное. Но это должно быть true, чтобы потоки контекста.