ASP.NET Core中如何在新建宿主内复用现有DI容器
实现子宿主复用主宿主DI容器的方案
核心原理
ASP.NET Core的依赖注入体系原生支持父子容器层级结构,无需重复注册主宿主的所有服务,只需将主宿主已构建完成的根IServiceProvider作为子宿主容器的父级即可,完全避免重复注册带来的性能和内存损耗。
实现步骤
1. 调整后台服务字段存储主宿主根服务提供者
你不需要存储IServiceCollection,你的BackgroundWebHost作为主宿主托管的后台服务,可以直接在构造函数注入主宿主的根IServiceProvider,这是已经完成所有服务注册的最终容器实例:
public class BackgroundWebHost : BackgroundService { // 存储主宿主的根服务提供者 private readonly IServiceProvider _rootServiceProvider; public BackgroundWebHost(IServiceProvider rootServiceProvider) { _rootServiceProvider = rootServiceProvider; }
2. 自定义服务提供工厂绑定父子容器
创建自定义的IServiceProviderFactory实现,构建子宿主的容器时指定父级为_rootServiceProvider:
public class HierarchicalServiceProviderFactory : IServiceProviderFactory<IServiceCollection> { private readonly IServiceProvider _parentProvider; private readonly ServiceProviderOptions _options; public HierarchicalServiceProviderFactory(IServiceProvider parentProvider, ServiceProviderOptions options = null) { _parentProvider = parentProvider ?? throw new ArgumentNullException(nameof(parentProvider)); _options = options ?? new ServiceProviderOptions(); } public IServiceCollection CreateBuilder(IServiceCollection services) { return services; } public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder) { // 构建子容器时绑定父容器 return containerBuilder.BuildServiceProvider(_options, _parentProvider); } }
3. 修改子宿主创建逻辑,使用自定义工厂
调整你的CreateHost方法,指定使用刚才实现的层级服务提供工厂:
private IHost CreateHost(string[] args) { return Host.CreateDefaultBuilder(args) // 注册子宿主自身需要的独有服务(如果有) .ConfigureServices((ctx, services) => { // 这里只需要注册子宿主特有的服务,不需要重复注册主宿主的服务 }) // 使用自定义的层级服务提供工厂,绑定主宿主根容器为父级 .UseServiceProviderFactory(new HierarchicalServiceProviderFactory(_rootServiceProvider)) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => { options.ListenAnyIP(4999, listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; }); }); webBuilder.UseStartup<Startup>(); }) .Build(); }
注意事项
- 生命周期继承:父容器的单例服务全局共享,子容器的单例仅在子宿主范围内生效,子宿主销毁时不会释放父容器的任何服务
- 服务覆盖规则:如果子容器注册了和父容器同类型的服务,子宿主内解析时会优先使用子容器的注册
- 避免内存泄漏:不要在子容器中长时持有父容器的范围服务,如需使用请通过
IServiceScopeFactory创建独立范围解析 - 如果你必须使用已拿到的
IServiceCollection,请确保主宿主已经完成Build()操作生成IServiceProvider后再创建子宿主,否则主容器的服务注册不完整
内容的提问来源于stack exchange,提问作者tvandinther
相关产品推荐
相关产品推荐

