Подтвердить что ты не робот

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 не подходит для решения этой проблемы, так как мой коллега требует статичности из-за некоторых плохих проектных решений.

Большое спасибо

4b9b3361

Ответ 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, чтобы потоки контекста.