使用附加服务创建子范围

Create child scope with additional services

使用 Autofac 可以很容易地创建容器的子作用域并注册附加服务。我如何使用 .net 的依赖注入实现同样的效果?

我试图注入需要创建子容器的 class 的 IServiceProvider 只提供了 CreateScope() 方法来创建 agian 只有的 IServiceScope ServiceProvider 属性 并且没有办法注册额外的服务。还有什么我应该注入的东西可以让我在容器中注册更多服务吗?

我不知道 Autofac 的工作原理,但我可以向您解释 Microsoft DI 的工作原理。

首先,Microsoft DI 容器的设计让您有两个主要的抽象:

  • IServiceCollection:这是您用来在应用程序中注册服务的对象。将其视为实际 DI 容器的构建器对象。
  • ServiceProvider:这是实际的 DI 容器,您通过调用 IServiceCollection.BuildServiceProvider 扩展方法从 IServiceCollection 对象获得。 IServiceProvider接口描述了这个对象的行为(ServiceProviderclass实现了IServiceProvider接口)

所以使用 DI 容器是一个两步操作:首先你需要一个 IServiceCollection 对象,这样你就可以通过指定实现类型和生命周期(瞬态、作用域或单例)来注册服务, 然后你可以构建一个 ServiceProvider 并用它来解析你应用程序中的服务。 IServiceCollection 接口实际使用的具体类型是 ServiceCollection class.

当您构建服务集合以获得 ServiceProvider 实例时,您实际上获得了应用程序的根容器。之所以称为根容器,是因为您可以创建服务提供者的层次结构,其根位于应用程序的根容器中。

给定您的应用程序的根容器,为了创建子容器,您需要创建一个 scope,它基本上是一个用于解析服务的范围。每个作用域都有自己的容器,它是一个实现 IServiceProvider 接口的对象,您可以使用它来解析该作用域内的服务。

这是执行此操作的一般模式:

// create the IServiceCollection instance
var services = new ServiceCollection();

// register services on the IServiceCollection instance
services.AddSingleton<IFooService, FooService>();
services.AddScoped<IBarService, BarService>();
services.AddTransient<IBuzzService, BuzzService>();

// create the root container
using var rootContaier = services.BuildServiceProvider();

// create a scope
using var scope = rootContainer.CreateScope();

// gets a reference to the container for the scope
var scopeContainer = scope.ServiceProvider;

// use the scope container to resolve services
var fooService = scopeContainer.GetRequiredService<IFooService>();
var barService = scopeContainer.GetRequiredService<IBarService>();
var buzzService = scopeContainer.GetRequiredService<IBuzzService>();

// do whatever you want with the resolved services
fooService.Foo();
barService.Bar();
buzzService.Buzz();

这些依赖解析范围非常重要,因为它们定义了使用范围服务提供者解析的服务的生命周期。这些是规则:

  1. 使用单例 生命周期注册的服务总是 由应用程序根容器解析。即使使用子容器来解析单例服务,实际的服务解析也会委托给根容器。实现 IDisposable 接口的单例服务在处理根容器时被处理,这通常发生在应用程序关闭时。每个单例服务实际上从根容器解析一次,并且同一个实例在应用程序的整个生命周期中重复使用。
  2. 使用 transient 生命周期注册的服务基本上与单例服务相反。 每次 解析服务时,都会创建一个全新的实例。给定范围,每次范围服务提供者用于解析瞬态服务时,范围服务提供者都会创建并跟踪实现类型的全新实例。 释放范围时,从该范围解析并具有一次性实现类型的所有瞬态服务也将被释放
  3. scoped 生命周期注册的服务在其创建范围内表现得像单例。 如果您有范围并且使用范围服务提供者解析范围服务,则仅在第一次解析服务时创建实现类型的全新实例所有 解析 same 范围内的 same 服务的后续请求将得到满足 通过重用第一次在范围 中解析服务时创建的实现类型的实例。作用域服务由作用域跟踪:这很有用,因为实现类型为一次性的作用域服务将在作用域被释放时被释放(与瞬态服务相同的行为)。

从前面的规则可以推导出第四条规则:从不使用根容器来解析服务,总是创建作用域并且从该范围解析服务(记得在完成后释放该范围)。

您应该这样做,因为作用域和瞬态服务由作用域跟踪并在作用域被释放时被释放。如果您尝试从根容器解析瞬态和作用域服务,它们将被根容器跟踪,并且只有在根容器被释放时它们才会被释放:这通常发生在应用程序关闭时,因为根容器的生命周期是基本上是应用程序的生命周期。换句话说,如果不遵守第四条规则,就会造成内存泄漏。

请注意,如果您使用带有布尔参数的 IServiceCollection.BuildServiceProvider 重载,您可以要求服务集合构建一个服务提供者,该服务提供者实际上检查范围内的服务是否从未从根容器中解析。通过这种方式,您将对第四条规则进行部分检查(仅检查范围内的服务是否存在来自根容器的危险解决方案)。

回到你的问题,服务注册阶段都是在同一个 IServiceCollection 实例上完成的,并且不知道服务解析范围的概念。范围仅在服务解析的后期有用,用于定义已解析服务的生命周期,如上所述。

从服务集合实例构建根容器后,服务注册阶段就完成了,据我所知,您只能解析服务,不允许进行其他注册。