能否在IServiceCollection.Configure()中使用IServiceProvider?
关于IServiceCollection.Configure()使用IServiceProvider及命令处理程序追加的解决方案
首先明确:默认的IServiceCollection.Configure<T>扩展方法确实没有直接接受Func<IServiceProvider, T>的重载——这是因为Configure的核心设计是绑定配置源(比如appsettings.json)到选项类,而非通过依赖注入解析服务来配置。不过我们有两种思路解决你的问题:一种是满足你对Configure中使用IServiceProvider的需求,另一种是更贴合你实际场景(多次追加命令处理程序到同一个工厂)的方案。
一、如何在配置选项时使用IServiceProvider
如果你确实需要在配置选项类时依赖其他服务,可以通过OptionsBuilder<T>的重载来实现,它支持接收IServiceProvider作为参数:
// 先通过AddOptions()获取OptionsBuilder,再使用带IServiceProvider的Configure重载 services.AddOptions<YourOptionsClass>() .Configure<IServiceProvider>((options, sp) => { // 在这里可以随意解析DI中的服务 var requiredService = sp.GetRequiredService<IYourDependency>(); options.SomeProperty = requiredService.GetData(); });
另外,PostConfigure方法也提供了类似的重载,适合在基础配置完成后做依赖服务的后续调整:
services.PostConfigure<YourOptionsClass>((options, sp) => { var anotherService = sp.GetService<IAnotherService>(); if (anotherService != null) { options.AnotherProperty = anotherService.CalculateValue(); } });
二、实现多次调用AddCommandHandlers追加处理程序到同一个CommandHandlerFactory
这其实不需要依赖Configure,我们可以通过DI的设计来优雅实现:
步骤1:设计CommandHandlerFactory依赖IServiceProvider
让工厂延迟从DI容器中获取所有已注册的命令处理程序,这样每次新增的处理程序都会被自动纳入:
public class CommandHandlerFactory { private readonly IServiceProvider _serviceProvider; public CommandHandlerFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } // 根据命令类型获取对应的处理程序 public ICommandHandler GetHandlerForCommand(Type commandType) { var handlerInterface = typeof(ICommandHandler<>).MakeGenericType(commandType); return (ICommandHandler)_serviceProvider.GetRequiredService(handlerInterface); } // 获取所有已注册的命令处理程序 public IEnumerable<ICommandHandler> GetAllHandlers() { return _serviceProvider.GetServices<ICommandHandler>(); } }
步骤2:注册工厂为单例
确保整个应用生命周期中只有一个工厂实例:
services.AddSingleton<CommandHandlerFactory>();
步骤3:编写AddCommandHandlers扩展方法
每次调用时扫描指定程序集,注册所有命令处理程序到DI容器:
public static IServiceCollection AddCommandHandlers(this IServiceCollection services, Assembly targetAssembly) { // 扫描程序集中所有实现ICommandHandler的非抽象类 var handlerTypes = targetAssembly.GetTypes() .Where(t => typeof(ICommandHandler).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface); foreach (var handlerType in handlerTypes) { // 注册为Scoped(可根据你的业务需求改为Transient/Singleton) services.AddScoped(typeof(ICommandHandler), handlerType); // 如果是泛型命令处理程序,同时注册对应的泛型接口 var genericHandlerInterface = handlerType.GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICommandHandler<>)); if (genericHandlerInterface != null) { services.AddScoped(genericHandlerInterface, handlerType); } } return services; }
步骤4:多次调用扩展方法追加处理程序
现在你可以针对不同程序集多次调用,所有处理程序都会被注册到DI,工厂在需要时能获取到全部:
// 从程序集A注册处理程序 services.AddCommandHandlers(typeof(AssemblyACommandHandler).Assembly); // 从程序集B追加处理程序 services.AddCommandHandlers(typeof(AssemblyBCommandHandler).Assembly);
这种方式的优势是完全贴合DI的设计原则,不需要额外的全局状态,所有处理程序的生命周期由DI容器统一管理,且天然支持多次追加。
内容的提问来源于stack exchange,提问作者Darek
相关产品推荐
相关产品推荐

