是否对在整个 ASP.NET Core 3 应用程序中使用完全相同的 HTTPContextAccessor 实例感到困惑?

Confused about using the very same HTTPContextAccessor instance through the entire ASP.NET Core 3 application?

我读过有关将 IHttpContextAccessor 配置为 services.AddSingleton 范围的信息,但我也读过它正在工作 "async local",而且我也知道异步的复杂工作ASP.Net,我的意思是,例如,如果控制器操作方法是 async,并且它 await 用于 async 调用,那么它可能会继续另一个线程,但神奇的是一些维护的线程绑定事物(如 HttpContext

我的具体用例:我必须向我的 EF Core DbContext 注入一个 MyConverter class,它在 OnModelCreating 中使用它。但是,此模型由 DbContext 缓存,因此任何后续请求,即使它具有 DbContext 的全新实例,也将使用相同的模型,因此相同的 MyConverter 实例。 (即使它已配置 services.AddTransient)。这个 MyConverter 有一个构造函数和一个注入的 IHttpContextAccessor,所以基于非常相似的原因,它实际上也是所有 DbContext/MyConverter 用法的单例。

问题

这个在第一个请求中创建的特定 HttpContextAccessor 实例将服务于 Web 应用程序生命周期中的所有后续请求。它会正常工作吗?这里有没有(并发)陷阱?

(如果我们使用单个或多个 HttpContextAccessor 实例,并不重要,我认为正确吗,因为 它实现了获取HttpContext 将使用正确的方法(包括异步本地线程切换陷阱等)到 return 并使用正确的 HttpContext?)

简短回答:注册为 services.AddHttpContextAccessor(),然后您可以在任何需要的地方注入 IHttpContextAccessor,只要您在请求的执行上下文中使用它,它就会起作用。例如,对于不是由 HTTP 请求发起的代码,您无法读取 HTTP 请求 headers。


您说得对 IHttpContextAccessor 应该注册为单例。 recommendation is to use AddHttpContextAccessor() extension method. See source code here 而不是自己做。它在内部将 HttpContextAccessor 注册为单例。

HttpContextAccessor can be found here 的代码,我也粘贴在下面:

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;
    }
}

由于 HttpContext getter 属性 returns 来自异步本地字段,您始终会获得执行上下文的本地 HttpContext

仅当 IHttpContextAccessor 已在 DI 中注册时,才会在 HttpContextFactory.Create() 中设置 HttpContext 字段。 Source.

并且 HttpContextFactory.Create() 从设置上下文的 [HostingApplication](https://github.com/aspnet/AspNetCore/blob/v2.2.5/src/Hosting/Hosting/src/Internal/HostingApplication.cs) 调用。