Вы не уверены, что используете тот же экземпляр HTTPContextAccessor во всем приложении ASP.NET Core 3? - PullRequest
0 голосов
/ 24 мая 2019

Я читал о настройке IHttpContextAccessor как services.AddSingleton scope, но я также читал о том, что работает "async local", и я также знаю о сложной работе async в ASP.Net, я имею в виду дляНапример, если метод действия контроллера равен async, и он await для вызова async, то он может продолжаться с другим потоком, но волшебным образом некоторые вещи, связанные с потоками, поддерживаются (например, HttpContext)

Мой конкретный вариант использования: мне нужно ввести класс MyConverter в мой EF Core DbContext, который использует его в OnModelCreating.Однако эта модель кэшируется DbContext, поэтому любой последующий запрос, даже если он будет иметь совершенно новый экземпляр DbContext, будет использовать ту же самую модель, как и тот же самый экземпляр MyConverter.(даже он настроил services.AddTransient).Этот MyConverter имеет конструктор и вставленный IHttpContextAccessor, поэтому, исходя из очень похожих причин, он фактически будет также единичным для всех DbContext/MyConverter случаев использования.

Вопрос

Этот конкретный экземпляр HttpContextAccessor, созданный в самом первом запросе, будет обслуживать все последующие запросы в жизненном цикле веб-приложения.Будет ли это работать правильно?Есть ли здесь (параллелизм) ловушка?

(Правильно ли я полагаю, что не имеет большого значения , если мы используем один или несколько HttpContextAccessor экземпляров, потому что его реализацияполучения HttpContext будет использовать правильный путь, включая прерывания асинхронного локального переключателя потоков и т. д., чтобы вернуться с правильным HttpContext?)

1 Ответ

2 голосов
/ 24 мая 2019

Краткий ответ: зарегистрируйтесь как services.AddHttpContextAccessor(), и тогда вы сможете ввести IHttpContextAccessor куда угодно, и это будет работать, пока вы используете его в контексте выполнения запроса. Например, вы не можете прочитать заголовки HTTP-запроса для кода, который не был инициирован HTTP-запросом.


Вы правы, что IHttpContextAccessor должен быть зарегистрирован как синглтон. Вместо того, чтобы делать это самостоятельно, рекомендует использовать метод расширения AddHttpContextAccessor(). См. исходный код здесь . Он внутренне регистрирует HttpContextAccessor как синглтон.

Код HttpContextAccessor можно найти здесь , который я также вставлю ниже:

public class HttpContextAccessor : IHttpContextAccessor
{
    private static AsyncLocal<HttpContextHolder> _httpContextCurrent = new AsyncLocal<HttpContextHolder>();

    public HttpContext HttpContext
    {
        get
        {
            return  _httpContextCurrent.Value?.Context;
        }
        set
        {
            var holder = _httpContextCurrent.Value;
            if (holder != null)
            {
                // Clear current HttpContext trapped in the AsyncLocals, as its done.
                holder.Context = null;
            }

            if (value != null)
            {
                // Use an object indirection to hold the HttpContext in the AsyncLocal,
                // so it can be cleared in all ExecutionContexts when its cleared.
                _httpContextCurrent.Value = new HttpContextHolder { Context = value };
            }
        }
    }

    private class HttpContextHolder
    {
        public HttpContext Context;
    }
}

Поскольку свойство getter HttpContext возвращается из асинхронного локального поля, вы всегда получаете HttpContext local для контекста выполнения.

Поле HttpContext устанавливается в HttpContextFactory.Create() только в том случае, если * DI зарегистрировано IHttpContextAccessor. Источник .

И HttpContextFactory.Create() вызывается из [HostingApplication](https://github.com/aspnet/AspNetCore/blob/v2.2.5/src/Hosting/Hosting/src/Internal/HostingApplication.cs), где контекст настроен.

...