如何从 autofac 容器解析依赖项(在嵌套 LifetimeScope 中注册)

Asa*_*Ali 2 c# asp.net autofac event-bus

有什么方法可以解决容器中的依赖关系(实际上是在嵌套的 LifetimeScope 中注册的,而不是在容器中注册的)?

实际执行说明:

我有包含当前用户的基本信息的ApplicationContext类(在其构造函数中注入)。IServiceProvider它首先解析IHttpContextAccessorfrom serviceProvider,然后从 中提取用户信息httpContextAccessor.HttpContextApplicationContext类被注入到所有存储库/服务中。

但是,在一些静态类中,我正在ApplicationContext从静态 IoC 类(包装在其中的 autofac 容器)解析类。我认为这是唯一的解决方案,因为我无法注入静态构造函数。

我正在实现事件总线,我为其创建了EventBusContext从事件数据接收用户信息的类。

ApplicationContext当它获取为 null 时(这意味着此执行不是从 Http Request 开始) ,类才会尝试解析并EventBusContext从中IServiceProvider提取用户信息。HttpContext

一旦EventBus类从 RabbitMQ 接收到事件,它就会创建EventBusConext类,将用户信息添加到其中,并将其动态注册到新创建的嵌套中LifetimeScope,然后解析该类EventHandler并调用 Handle 方法(通过反射)。

一切都很完美! 仅当使用任何从静态类EventHandler解析类的类时才会出现此问题,因为静态类在内部尝试从 autofac 容器(包装在其中)解析,但未能成功。ApplicationContextIoCIoCEventBusContext

Tra*_*lig 5

我将重点讨论开始时的最初问题:

有什么方法可以解决容器中的依赖关系(实际上是在嵌套的 LifetimeScope 中注册的,而不是在容器中注册的)?

最简洁的答案是不。如果在子作用域中注册/添加了某些内容,则父作用域将无法知道它。

Autofac 范围是分层的。有很多关于此的文档,包括一个图表来说明这一点。

试图将其归结为“规则”,我们可以说:

  • 报名“规则”:
    • 您可以将事物注册到根容器中。
    • 创建子生命周期范围时,您可以添加仅针对该范围存在的新注册。
  • 决议“规则”:
    • 当您解析服务时,它只会知道您正在解析的生命周期范围内注册的事物以及任何父级。
    • 如果您解析单例,它将始终从根生命周期范围解析,包括其所有依赖项。
    • 子作用域了解父作用域。
    • 父作用域不保存子作用域的引用或“了解”子作用域。

这是非常简单的,但足够接近来说明这一点。

如果您将某些内容动态添加到子生命周期作用域中,则您只能在该子生命周期作用域以及您从那里创建的任何子作用域中访问它们。

var builder = new ContainerBuilder();
builder.RegisterType<A>();
var container = builder.Build();

// This will work (though it's recommended you avoid resolving things
// from containers because it can lead to memory leaks).
// https://autofac.readthedocs.io/en/latest/lifetime/index.html
container.Resolve<A>();

// This will NOT work. The container doesn't have B in it.
container.Resolve<B>();

using(var scope1 = container.BeginLifetimeScope(b => b.RegisterType<B>())
{
  // This will work. The child scope knows about parent registrations.
  scope1.Resolve<A>();

  // This will also work. The registration was added to the scope.
  scope1.Resolve<B>();

  // This will STILL NOT WORK. The parent does NOT know about child scopes.
  container.Resolve<B>();

  using(var scope2 = scope1.BeginLifetimeScope())
  {
    // These will work. The child scope knows about parent registrations.
    scope2.Resolve<A>();
    scope2.Resolve<B>();

    // This will STILL NOT WORK.
    container.Resolve<B>();
  }
}
Run Code Online (Sandbox Code Playgroud)

对此没有任何黑客手段或解决方法。这是故意分层的。

如果您有一些需要访问请求级项目的内容(或者需要在子生命周期范围中动态注册的内容),获取它们的唯一方法是从该子生命周期范围或嵌套子项目中进行解析。

更具体地实现目标:

从实现描述来看,您有一些故意在请求之外运行并从根容器解析的东西(同样,这不是一个好主意,但事实就是如此)。如果这些东西突然需要通过请求动态注册的项目,那么您基本上有两个选择,这两个选择都是“您需要更改您的架构/设计”。

  1. 将所有在“请求外部”运行的项目切换为“在请求内部”运行,以便它们可以访问动态注册的内容。
  2. 更改方法签名或其他任何内容,以便动态注册的事物不再依赖于“请求之外”的项目 - 使这些动态事物作为方法参数传入。

实际上提供一个完全更新的设计或“工作代码”来说明这些事情中的任何一个,更多的是一种咨询工作,而不是问题的答案。但不幸的是,获取每个请求数据的解决方案意味着您需要更改您的设计。