【问题标题】:Singleton with AsyncLocal vs Scope Service带有 AsyncLocal 与 Scope 服务的单例
【发布时间】:2019-11-07 19:12:08
【问题描述】:

我查看了如何在 .NET Core 中创建 HttpContext。然后我发现有一个名为HttpContextFactory 的类,它创建HttpContext 对象并将其分配到HttpContextAccessor 类的HttpContext 属性中。为了在我们的代码中使用 HttpContext 对象,我们将 IHttpContextAccessor 注入到需要该对象的类的构造函数中。

当我查看 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 一起使用。

【问题讨论】:

    标签: c# asp.net-core .net-core httpcontext


    【解决方案1】:

    我猜原因之一可能是 Asp.Net Core IServiceProvider 不允许在 Singleton 类中注入 Scoped Dependency。这可能是其背后的重大决定。如果事情按照您的建议进行了限定,那么所有使用它的类可能都必须限定范围。但有趣的是,一旦请求得到处理,HTTPContext 就会变为 null。

    【讨论】:

      【解决方案2】:

      只要是单例,解析的IHttpContextAccessor 实例就可以被单例服务永久持有并正常工作,而如果单例服务解析范围内的IHttpContextAccessor,则可能会导致问题。

      【讨论】:

      • 如果您查看此文件github.com/aspnet/AspNetCore/blob/master/src/Http/Http/src/…,我认为您误解了我的问题。 IHttpContextAccessor 注册为单例。这是因为他们使用AsyncLocal 将HttpContext 对象存储在HttpContextAccessor 类中。我在问,如果 IHttpContextAccessor 被注册为作用域,并且 AsyncLocal 不用于将 HttpContext 存储在 HttpContextAccessor 类中,那有什么区别。
      • DefaultHttpContextFactory 本身没有注册为Singleton,而是注册为Transient,正如您在此文件github.com/aspnet/AspNetCore/blob/… 中看到的那样。这个类负责设置HttpContext的值。
      • 如果你的意思是,这个类被设置为单例,这样每当我们在我们的项目中创建我们自己的单例类时,我们就可以在那里注入IHttpContextAccessor。这有点道理 :) 但是我可以问一下我们什么时候需要从我们的 Web 项目中的单例类访问 IHttpContextAccessor 的场景吗?
      • 对于一些可能有一些方法必须在请求中调用的有状态服务(这意味着对象具有单例生命周期,而方法调用是作用域的),将其设为一个可能很有用服务而不是依赖于单例存储服务的范围服务。
      • @Alsien 是否有可能是因为 asp.net 核心中的 IServiceProvider 不允许在单例中进行范围服务注入,所以这就是为什么 HttpContextAccessor 是使用 asynclocal 而不是建议的 Scoped 的原因。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-30
      • 2017-08-18
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      相关资源
      最近更新 更多