ASP.NET Core 3.1插件如何委托外部DI容器解析未知服务?
解决ASP.NET Core插件DI回退到外部容器的方案
当然可以做到!你想要的「内置DI找不到服务就委托给外部容器」的逻辑,完全可以通过自定义ASP.NET Core的服务提供器(ServiceProvider)来实现,不用再手动逐个注册服务。下面是具体的实现步骤和细节:
1. 实现回退式服务提供器
首先,我们需要一个包装类,把ASP.NET Core内置的服务提供器和你的外部容器_externalContainer结合起来。这个类会先尝试用内置DI解析服务,失败时再委托给外部容器:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; // 这里的IContainer替换成你外部容器的实际类型(比如Autofac.IContainer、Unity.IUnityContainer等) public class FallbackServiceProvider : IServiceProvider { private readonly IServiceProvider _innerAspNetProvider; private readonly IContainer _externalContainer; public FallbackServiceProvider(IServiceProvider innerAspNetProvider, IContainer externalContainer) { _innerAspNetProvider = innerAspNetProvider; _externalContainer = externalContainer; } public object GetService(Type serviceType) { // 特殊处理IEnumerable<T>类型的批量解析 if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { var elementType = serviceType.GetGenericArguments()[0]; // 先获取内置DI中的所有实例 var innerInstances = (IEnumerable)_innerAspNetProvider.GetService(serviceType) ?? Array.CreateInstance(elementType, 0); var combinedInstances = innerInstances.Cast<object>().ToList(); // 尝试从外部容器获取实例(兼容两种情况:单个实例、枚举实例) try { // 获取单个实例 var externalSingle = _externalContainer.Resolve(elementType); if (externalSingle != null) combinedInstances.Add(externalSingle); // 获取枚举实例(如果外部容器支持的话) var externalEnumerable = (IEnumerable)_externalContainer.Resolve(serviceType); if (externalEnumerable != null) combinedInstances.AddRange(externalEnumerable.Cast<object>()); } catch { // 外部容器不支持该类型解析时,忽略异常,保持原有行为 } return combinedInstances.AsEnumerable(); } // 普通服务的解析逻辑:先内置,后外部 var innerService = _innerAspNetProvider.GetService(serviceType); if (innerService != null) return innerService; try { return _externalContainer.Resolve(serviceType); } catch (Exception ex) { // 抛出和内置DI一致的异常类型,避免破坏原有错误处理逻辑 throw new InvalidOperationException($"无法解析服务类型: {serviceType.FullName}", ex); } } }
2. 实现服务提供器工厂
为了让ASP.NET Core的WebHost使用我们的自定义服务提供器,需要实现IServiceProviderFactory接口,这是官方推荐的自定义服务提供器的扩展方式:
using Microsoft.Extensions.DependencyInjection; public class FallbackServiceProviderFactory : IServiceProviderFactory<IServiceCollection> { private readonly IContainer _externalContainer; public FallbackServiceProviderFactory(IContainer externalContainer) { _externalContainer = externalContainer; } // 直接返回原服务集合,不需要修改内置的注册逻辑 public IServiceCollection CreateBuilder(IServiceCollection services) { return services; } // 创建自定义的回退服务提供器 public IServiceProvider CreateServiceProvider(IServiceCollection services) { var innerAspNetProvider = services.BuildServiceProvider(); return new FallbackServiceProvider(innerAspNetProvider, _externalContainer); } }
3. 配置WebHost使用自定义工厂
最后,在你的WebHostBuilder配置中,替换默认的服务提供器工厂为我们的实现即可:
new WebHostBuilder() .UseKestrel() .UseStartup<MyStartup>() // 注册自定义服务提供器工厂,传入外部容器实例 .UseServiceProviderFactory(new FallbackServiceProviderFactory(_externalContainer));
关键细节说明
- 生命周期管理:外部容器中的服务生命周期由外部容器自行管理,ASP.NET Core的内置DI不会负责它们的释放。如果这些服务需要释放,要确保外部容器在合适的时机(比如插件卸载时)处理。
- 验证逻辑:如果你的WebHost开启了
ValidateOnBuild(默认关闭),内置DI只会验证自身注册的服务,不会检查外部容器的服务,这是正常行为,因为外部服务不在内置DI的注册列表中。 - 异常处理:我们在代码中捕获了外部容器的解析异常,并转换为内置DI的标准异常,这样不会破坏ASP.NET Core原有的错误处理流程。
这样一来,你的插件中的控制器、过滤器、中间件等组件,在构造函数中依赖的服务,只要外部容器能解析,就不需要手动注册到内置DI中了。
内容的提问来源于stack exchange,提问作者Impworks
相关产品推荐
相关产品推荐

