是否对在整个 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)
调用。
我读过有关将 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)
调用。