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

标签 c# asp.net autofac event-bus

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

实际实现说明:

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

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

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

ApplicationContext 类尝试从 IServiceProvider 解析 EventBusContext 并从中提取用户信息ONLY(如果获取) HttpContext 为 null(这意味着此执行不是从 Http 请求开始)。

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

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

最佳答案

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

Is there any way to resolve the dependency (which is actually registered in nested LifetimeScope, not in container) from container?

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

Autofac 作用域是分层的。 There is a lot of documentation on this including a diagram to illustrate that.

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

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

这非常简单,但足以说明这一点。

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

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>();
  }
}

没有任何黑客手段或解决方法可以解决此问题。它是故意分层的。

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

更加具体地实现目标:

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

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

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

关于c# - 如何从 autofac 容器解析依赖项(在嵌套 LifetimeScope 中注册),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59521985/

相关文章:

asp.net - Linq to SQL - 如何在没有两个查询的情况下分页时获取结果总数

c# - 使用 Autofac 的 IIndex 解析多个 Keyed 实例

c# - Autofac:注册组件并根据解析父解析

xamarin.android - 有什么方法可以在 Xamarin.Android 项目中使用 Autofac 使用构造函数注入(inject)?

c# - 将 base64 字符串转换为图像 C# 时出错

c# - EF6 操作无法完成,因为 DbContext 已被释放

c# - .NET MAUI - Controller 句柄地址

asp.net - CSS 溢出自动不起作用

c# - 如何在 C# 中评论/记录覆盖?

c# - 将字典分配给键值