![](/img/trans.png)
[英]How to dynamically resolve InstancePerLifetimeScope dependency in ASP.NET Core?
[英]How can I inject an InstancePerLifetimeScope object into a SingleInstance object outside of ASP.NET?
我有一个使用Autofac的控制台应用程序。 该应用程序接受来自AMQP队列的消息并对其进行填充。 每次收到消息时,应用程序都会创建一个新的Autofac生存期范围并在其中注册一些内容。 值得注意的是,一个包含有关消息和触发消息的用户的信息的类(我们称其为Context
)。 大多数实际功能(具有令人讨厌的大依赖关系树的一组服务类)在根Autofac范围中注册为InstancePerLifetimeScope
。 这会导致大量的内存分配和一些延迟,因为每次接收到消息时都会重新实例化所有这些类。
我想将大多数服务类转换为SingleInstance
依赖项,因为从概念上讲它们只是功能的容器,并且没有注入依赖项的状态。 Autofac不允许我这样做,因为我要注入的Context
对象是InstancePerLifetimeScope
。 很好,由于明显的原因,它不起作用(上下文在收到一条消息后会过时,并且Autofac无法在不重新实例化整个对象的情况下重新注入它)。
但是,即使我注入Func<Context>
或ContextProvider
对象,情况也是如此。 我知道这是因为Autofac仍在寻找应该在我注册SingleInstance
类的根范围中返回的Context
,而不是在当前生命周期范围内返回的Context
。
因此, 有没有办法让Autofac注入一个执行类似() => GetCurrentInnermostLifetimeScope().Resolve<Context>()
的函数?
我还看到了一些其他的SO问题,但是它们都分为两类:“将所有内容注册为InstancePerLifetimeScope
”(我试图避免),或者“使用ASP.NET的DependencyResolver.Current
”(在控制台应用程序中不重要)。
有几种方法可以做到这一点,但是如果不超过您当前的做法,它们可能会招致同样多的费用。 在尝试使所有内容成为单例之前,我建议进行概要分析并找到实际上会影响性能的内容。 这种请求听起来像是过早的优化,并且也突破了一些明确定义的模式。
我想将大多数这些服务类转换为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<T>
或[ThreadStatic]
也会产生不小的成本,并且可能比您认为遇到的GC压力还要大。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.