【问题标题】:How can I inject an InstancePerLifetimeScope object into a SingleInstance object outside of ASP.NET?如何将 InstancePerLifetimeScope 对象注入到 ASP.NET 之外的 SingleInstance 对象中?
【发布时间】:2019-09-11 19:01:27
【问题描述】:

我有一个使用 Autofac 的控制台应用程序。此应用程序接受来自 AMQP 队列的消息并处理它们。每次收到消息时,应用程序都会创建一个新的 Autofac 生命周期范围并在其中注册一些内容;值得注意的是,一个类(我们称之为Context)包含有关消息和触发它的用户的信息。大多数实际功能(一组具有令人讨厌的大依赖树的服务类)在根 Autofac 范围内注册为InstancePerLifetimeScope。这会导致大量内存分配和一些延迟,因为每次收到消息时都会重新实例化所有这些类。

我想将这些服务类中的大多数转换为SingleInstance 依赖项,因为它们在概念上只是函数的容器,并且除了注入其中的依赖项之外没有任何状态。 Autofac 不会让我这样做,因为我注入的 Context 对象是 InstancePerLifetimeScope。没关系,由于显而易见的原因,这不应该起作用(上下文会在一条消息后过时,Autofac 无法在不重新实例化整个对象的情况下重新注入它)。

但是,即使我注入 Func<Context>ContextProvider 对象也是如此。我知道这是因为 Autofac 仍在寻找 Context,它们应该在我注册 SingleInstance 类的根范围内返回,而不是在当前生命周期范围内。

那么,有没有办法让 Autofac 注入一个类似 () => GetCurrentInnermostLifetimeScope().Resolve<Context>() 的函数?

我已经看到其他一些关于此的 SO 问题,但它们都分为两类:要么“将所有内容注册为 InstancePerLifetimeScope”(我试图避免),要么“使用 ASP.NET 的 @987654332 @"(在控制台应用程序中不是这样)。

【问题讨论】:

    标签: c# autofac


    【解决方案1】:

    有几种方法可以做到这一点,但如果不超过您目前的做法,它们可能会产生同样多的成本。在尝试使 everything 成为单例之前,我建议进行概要分析并找到真正影响性能的那些。这种请求听起来像是过早的优化,而且还打破了一些定义明确的模式。

    我想将这些服务类中的大多数转换为 SingleInstance 依赖项,因为它们在概念上只是函数的容器,并且除了注入其中的依赖项之外没有任何状态。

    这意味着 有状态。为什么他们没有任何其他状态很重要?例如,它们的依赖项之一可能具有因此需要将父级范围保持为每个请求的状态。生命周期范围可帮助您确定可以拥有单例的层,而您的似乎没有。

    话虽如此,有办法解决这个问题。我将举一个类似于 ASP.NET Core 通过单例提供对HttpContext 的访问方式的示例。这假设您正在使用基于Task 的处理;如果没有,那么这将无济于事,但可能会被调整。有些东西必须提供上下文,我将使用AsyncLocal<T>;如果每个请求都有自己的线程,你可以试试[ThreadStatic]

    using Autofac;
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace InstanceRequest
    {
        class Program
        {
            static async Task Main(string[] args)
            {
                var builder = new ContainerBuilder();
    
                builder.RegisterType<ScopeAccessor>()
                    .AsSelf()
                    .As<IResolver>()
                    .SingleInstance();
    
                builder.RegisterType<SingleInstance>()
                    .SingleInstance();
    
                builder.RegisterType<IdAccessor>()
                    .InstancePerLifetimeScope();
    
                builder.RegisterType<Request>()
                    .InstancePerLifetimeScope();
    
                using (var container = builder.Build())
                {
                    await RunAsync(container);
                }
            }
    
            private static async Task RunAsync(IContainer container)
            {
                var accessor = container.Resolve<ScopeAccessor>();
    
                var tasks = Enumerable.Range(0, 100).Select(async id =>
                {
                    using (var scope = container.BeginLifetimeScope())
                    {
                        accessor.CurrentScope = scope;
    
                        await scope.Resolve<Request>().RunAsync();
                    }
                });
    
                await Task.WhenAll(tasks);
            }
        }
    
        class Request
        {
            private readonly IdAccessor _id;
            private readonly SingleInstance _s;
    
            public Request(IdAccessor id, SingleInstance s)
            {
                _id = id;
                _s = s;
            }
    
            public async Task RunAsync()
            {
                while (true)
                {
                    var fromScoped = _id.Id;
                    var fromSingleton = _s.Id;
    
                    if (fromScoped != fromSingleton)
                    {
                        throw new InvalidOperationException();
                    }
    
                    Console.WriteLine($"{fromScoped} == {fromSingleton}");
    
                    await Task.Delay(100);
                }
            }
        }
    
        class SingleInstance
        {
            private readonly IResolver _resolver;
    
            public SingleInstance(IResolver resolver)
            {
                _resolver = resolver;
            }
    
            public int Id => _resolver.Resolve<IdAccessor>().Id;
        }
    
        interface IResolver
        {
            T Resolve<T>();
        }
    
        class ScopeAccessor : IResolver
        {
            private readonly AsyncLocal<ILifetimeScope> _scope = new AsyncLocal<ILifetimeScope>();
    
            public ILifetimeScope CurrentScope
            {
                get => _scope.Value;
                set => _scope.Value = value;
            }
    
            public T Resolve<T>() => CurrentScope.Resolve<T>();
        }
    
        class IdAccessor
        {
            private static int _id = 0;
    
            public IdAccessor()
            {
                Id = Interlocked.Increment(ref _id);
            }
    
            public int Id { get; }
        }
    }
    

    不过,我会注意到,使用 AsyncLocal&lt;T&gt;[ThreadStatic] 之类的东西也会产生不小的成本,并且可能超过您认为自己正在经历的 GC 压力。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-16
      • 2019-05-28
      • 1970-01-01
      • 1970-01-01
      • 2010-11-27
      相关资源
      最近更新 更多