具有 AsyncLocal 与范围服务的单例
Singleton with AsyncLocal vs Scope Service
我查看了如何在 .NET Core 中创建 HttpContext。然后我发现有一个名为 HttpContextFactory
的 class 创建并分配 HttpContext
对象到 HttpContext
属性 of HttpContextAccessor
class.为了在我们的代码中使用 HttpContext 对象,我们将 IHttpContextAccessor 注入到需要该对象的 class 的构造函数中。
当我查看 HttpContextAccessor 的实现时,显然它的 HttpContext 属性 从私有 AsyncLocal
变量获取 HttpContext 对象值,后来 HttpContextAccessor 被注册为 Singleton.
https://github.com/aspnet/AspNetCore/blob/master/src/Http/Http/src/HttpContextAccessor.cs
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
namespace Microsoft.AspNetCore.Http
{
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;
}
}
}
我很好奇,这样做而不是使用 Scope 服务有什么好处?在我看来,两者都会使对象在请求范围内可用。
如果它是范围服务,我认为 HttpContextAccessor 看起来像这样
using System.Threading;
namespace Microsoft.AspNetCore.Http
{
public class HttpContextAccessor : IHttpContextAccessor
{
private HttpContextHolder _httpContextCurrent;
public HttpContext HttpContext
{
get
{
return _httpContextCurrent?.Context;
}
set
{
if (value != null)
{
_httpContextCurrent = new HttpContextHolder { Context = value };
}
}
}
private class HttpContextHolder
{
public HttpContext Context;
}
}
}
然后将其用作范围服务
services.TryAddScope<IHttpContextAccessor, HttpContextAccessor>();
我想知道每种方法的优缺点,以便在为我的项目创建库时了解何时将 Singleton 与 AsyncLocal 或 Scope 结合使用。
只要是单例,已解析的 IHttpContextAccessor
实例就可以由单例服务永久持有并正常工作,而如果单例服务解析范围 IHttpContextAccessor
则可能会导致问题.
我想原因之一可能是 Asp.Net 核心 IServiceProvider 不允许将作用域依赖项注入单例中 class。这可能是其背后的一个重大决定。如果按照您的建议确定了范围,那么所有使用它的 classes 可能都必须确定范围。但有趣的是,一旦请求被送达,HTTPContext 就变为 null。
我查看了如何在 .NET Core 中创建 HttpContext。然后我发现有一个名为 HttpContextFactory
的 class 创建并分配 HttpContext
对象到 HttpContext
属性 of HttpContextAccessor
class.为了在我们的代码中使用 HttpContext 对象,我们将 IHttpContextAccessor 注入到需要该对象的 class 的构造函数中。
当我查看 HttpContextAccessor 的实现时,显然它的 HttpContext 属性 从私有 AsyncLocal
变量获取 HttpContext 对象值,后来 HttpContextAccessor 被注册为 Singleton.
https://github.com/aspnet/AspNetCore/blob/master/src/Http/Http/src/HttpContextAccessor.cs
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
namespace Microsoft.AspNetCore.Http
{
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;
}
}
}
我很好奇,这样做而不是使用 Scope 服务有什么好处?在我看来,两者都会使对象在请求范围内可用。
如果它是范围服务,我认为 HttpContextAccessor 看起来像这样
using System.Threading;
namespace Microsoft.AspNetCore.Http
{
public class HttpContextAccessor : IHttpContextAccessor
{
private HttpContextHolder _httpContextCurrent;
public HttpContext HttpContext
{
get
{
return _httpContextCurrent?.Context;
}
set
{
if (value != null)
{
_httpContextCurrent = new HttpContextHolder { Context = value };
}
}
}
private class HttpContextHolder
{
public HttpContext Context;
}
}
}
然后将其用作范围服务
services.TryAddScope<IHttpContextAccessor, HttpContextAccessor>();
我想知道每种方法的优缺点,以便在为我的项目创建库时了解何时将 Singleton 与 AsyncLocal 或 Scope 结合使用。
只要是单例,已解析的 IHttpContextAccessor
实例就可以由单例服务永久持有并正常工作,而如果单例服务解析范围 IHttpContextAccessor
则可能会导致问题.
我想原因之一可能是 Asp.Net 核心 IServiceProvider 不允许将作用域依赖项注入单例中 class。这可能是其背后的一个重大决定。如果按照您的建议确定了范围,那么所有使用它的 classes 可能都必须确定范围。但有趣的是,一旦请求被送达,HTTPContext 就变为 null。