t3c*_*b0t 9 c# dependency-injection .net-6.0
使用 Autofac,创建容器的子范围并注册附加服务非常容易。我如何通过 .net 的依赖注入实现相同的目标?
我IServiceProvider尝试注入到需要创建子容器的类中,仅提供了CreateScope()创建IServiceScopeagian 的方法,该方法仅具有该ServiceProvider属性,而无法注册其他服务。我还应该注入其他什么东西来允许我向容器注册更多服务吗?
Enr*_*one 12
我不知道 Autofac 是如何工作的,但我可以向你解释 Microsoft DI 是如何工作的。
首先,Microsoft DI 容器的设计使您拥有两个主要抽象:
IServiceCollection:这是您用来在应用程序中注册服务的对象。将此视为实际 DI 容器的构建器对象。ServiceProvider:这是实际的 DI 容器,您可以IServiceCollection通过调用IServiceCollection.BuildServiceProvider扩展方法从对象中获取它。该对象的行为由接口描述IServiceProvider(ServiceProvider类实现IServiceProvider接口)因此,使用 DI 容器是一个两步操作:首先,您需要一个IServiceCollection对象,以便您可以通过指定实现类型和生命周期(瞬态、作用域或单例)来注册服务,然后您可以构建一个对象ServiceProvider并使用它来解决您的应用程序中的服务。接口实际使用的具体类型IServiceCollection是ServiceCollection类。
当您构建服务集合以获取实例时ServiceProvider,您实际上获得了应用程序的根容器。它被称为根容器,因为您可以创建一个服务提供者的层次结构,其根位于应用程序的根容器中。
给定应用程序的根容器,为了创建子容器,您需要创建一个作用域,它基本上是用于解析服务的作用域。每个范围都有自己的容器,它是一个实现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();
Run Code Online (Sandbox Code Playgroud)
这些依赖解析范围非常重要,因为它们定义了使用范围服务提供者解析的服务的生命周期。这些是规则:
IDisposable接口的单例服务在根容器被释放时被释放,这通常发生在应用程序关闭时。每个单例服务实际上从根容器解析一次,并且在应用程序的整个生命周期中重复使用同一个实例。从前面的规则中,您可以得出第四条规则:永远不要使用根容器来解析服务,始终创建一个作用域并从该作用域解析服务(记住在完成后处置该作用域)。
您应该这样做,因为作用域和瞬态服务由作用域跟踪并在作用域释放时释放。如果您尝试从根容器解析瞬态和作用域服务,它们将由根容器跟踪,并且仅在根容器被处置时才会被处置:这通常发生在应用程序关闭时,因为根容器的生命周期是基本上是应用程序的生命周期。换句话说,如果不遵守第四条规则,就会造成内存泄漏。
请注意,如果您使用采用布尔参数的重载IServiceCollection.BuildServiceProvider,您可以要求服务集合构建一个服务提供程序,该服务提供程序实际上检查范围服务是否从未从根容器解析。这样,您将获得对第四条规则的部分检查(仅检查范围服务是否来自根容器的危险解决方案)。
回到你的问题,服务注册阶段都是在同一个IServiceCollection实例上完成的,并且不知道服务解析范围的概念。范围仅在服务解析的后期有用,用于定义已解析服务的生命周期,如上所述。
一旦您从服务集合实例构建了根容器,服务注册阶段就完成了,根据我的了解,您只能解析服务,不允许进行额外的注册。