如何用依赖注入创建带额外参数的工厂子对象无需工厂注入ILogger
结论
完全可以实现,不需要将ILogger<IIoTService>传入IoTServiceFactory构造函数即可完成实例创建,核心思路是分离「运行时动态生成的参数」和「容器管理的固定注入依赖」,两类参数分别在不同阶段传入,不需要工厂类持有所有依赖项。
实现方案1:使用ActivatorUtilities(.NET 标准DI原生支持,最推荐)
这个是.NET Core/.NET 5+ 自带依赖注入容器的内置工具类,不需要引入第三方包,创建实例时会自动从容器解析已注册的构造参数,仅需要手动传入运行时动态生成的参数即可。
- 服务注册阶段正常注册
ILogger相关依赖(.NET默认框架已经自动完成日志组件注册,不需要额外配置) - 工厂类仅需注入
IServiceProvider,不需要持有ILogger依赖,改造后代码如下:
public class IoTServiceFactory { private readonly IServiceProvider _serviceProvider; // 仅注入DI容器,不需要传入ILogger public IoTServiceFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task<IIoTService> CreateService(Parameters parameters, CancellationToken cancellationToken) { DeviceClient deviceClient = await SetupDeviceClientAsync(parameters, cancellationToken); // 自动从容器解析ILogger<IIoTService>,仅手动传入运行时生成的deviceClient return ActivatorUtilities.CreateInstance<IoTService>(_serviceProvider, deviceClient); } private Task<DeviceClient> SetupDeviceClientAsync(Parameters parameters, CancellationToken cancellationToken) { // 原有设备客户端初始化逻辑保持不变 throw new NotImplementedException(); } }
该方案注意点:
- 参数匹配规则:手动传入的参数会优先匹配构造函数中同类型的参数,剩余未手动传入的参数会自动从DI容器解析,不需要手动处理
- 可维护性强:后续
IoTService新增其他容器管理的依赖(比如配置服务、其他业务客户端)时,只要依赖已注册到DI容器,不需要修改工厂类任何代码即可自动完成注入 - 生命周期管控:如果
IoTService依赖Scoped生命周期的服务,在创建实例时手动开启DI作用域即可,避免根容器解析Scoped服务造成的内存泄漏问题,示例代码:
public async Task<IIoTService> CreateService(Parameters parameters, CancellationToken cancellationToken) { DeviceClient deviceClient = await SetupDeviceClientAsync(parameters, cancellationToken); using var scope = _serviceProvider.CreateScope(); return ActivatorUtilities.CreateInstance<IoTService>(scope.ServiceProvider, deviceClient); }
实现方案2:使用工厂委托(轻量无容器依赖场景)
如果不想让工厂类依赖DI容器抽象,可以在服务注册阶段提前绑定好固定依赖,仅暴露接收运行时参数的委托给工厂类使用。
- 服务注册阶段定义创建委托,提前从容器解析
ILogger:
// Program.cs服务注册逻辑中添加 services.AddSingleton<Func<DeviceClient, IIoTService>>(serviceProvider => { // 注册阶段就从容器拿到ILogger实例,不需要工厂类持有 var logger = serviceProvider.GetRequiredService<ILogger<IIoTService>>(); // 返回仅接收运行时参数的创建委托 return deviceClient => new IoTService(deviceClient, logger); });
- 工厂类仅注入上述委托即可,完全感知不到
ILogger的存在:
public class IoTServiceFactory { private readonly Func<DeviceClient, IIoTService> _serviceCreateDelegate; // 仅注入业务委托,不需要传入ILogger,也不依赖DI容器抽象 public IoTServiceFactory(Func<DeviceClient, IIoTService> serviceCreateDelegate) { _serviceCreateDelegate = serviceCreateDelegate; } public async Task<IIoTService> CreateService(Parameters parameters, CancellationToken cancellationToken) { DeviceClient deviceClient = await SetupDeviceClientAsync(parameters, cancellationToken); // 调用委托仅传入运行时参数即可完成实例创建 return _serviceCreateDelegate(deviceClient); } private Task<DeviceClient> SetupDeviceClientAsync(Parameters parameters, CancellationToken cancellationToken) { // 原有设备客户端初始化逻辑保持不变 throw new NotImplementedException(); } }
该方案优缺点:
- 优点:工厂类不需要依赖DI容器抽象,仅依赖业务定义的创建委托,完全符合显式依赖原则
- 缺点:后续
IoTService新增注入依赖时,需要同步修改服务注册阶段的委托逻辑,维护成本高于第一种方案
内容的提问来源于stack exchange,提问作者Mike Lenart
相关产品推荐
相关产品推荐

