如何为运行时实例化的类实现依赖注入?
问题描述
我有两个类:ImportBase.cs(父类)和ImportFleet.cs(子类),用于导入CSV文件。ImportBase的每个子类实现不同的具体导入逻辑,在Service类中运行时确定要实例化的子类并调用导入方法,这部分目前正常。但现在想把仓储类依赖注入到ImportBase及其子类中,遇到了运行时实例化子类无法使用DI的问题。
相关代码如下:
ImportBase.cs
namespace WebApi.Services.Import.Investments { interface IImport { public void Import(IFormFile file, int UserId); } public abstract class ImportBase : IImport { public abstract void Import(IFormFile file, int UserId); protected List<InvestmentTransactionType> transactionTypes = new(); protected IInvestmentEntityRepository _investmentEntityRepository; public ImportBase(IInvestmentEntityRepository investmentEntityRepository) { _investmentEntityRepository = investmentEntityRepository; } } }
ImportFleet.cs
namespace WebApi.Services.Import.Investments { public class ImportFleet : ImportBase { public ImportFleet(IInvestmentEntityRepository investmentEntityRepository) : base(investmentEntityRepository) { } public override void Import(IFormFile file, int UserId) { } } }
InvestmentService.cs
namespace WebApi.Services { public interface IInvestmentService { public void Import(IFormFile file, int UserId, int InvestmentEntityId); } public class InvestmentService: IInvestmentService { public void Import(IFormFile file, int UserId, int InvestmentEntityId) { IImport importService = null; string investmentEntity = ImportBase.determineInvestmentEntityFromCsv(file); switch(investmentEntity) { case "fleet": importService = new ImportFleet(); // 问题所在 break; } if (importService != null) { importService.Import(file, UserId); } } } }
问题出在importService = new ImportFleet();这行代码:因为只能在运行时确定要实例化的子类,无法直接利用依赖注入获取仓储类实例。
解决方案
方法1:通过IServiceProvider手动获取实例
在InvestmentService中注入IServiceProvider,运行时通过它获取对应的IImport实现类,DI会自动处理依赖注入:
修改InvestmentService
namespace WebApi.Services { public interface IInvestmentService { public void Import(IFormFile file, int UserId, int InvestmentEntityId); } public class InvestmentService: IInvestmentService { private readonly IServiceProvider _serviceProvider; // 注入IServiceProvider public InvestmentService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public void Import(IFormFile file, int UserId, int InvestmentEntityId) { IImport importService = null; string investmentEntity = ImportBase.determineInvestmentEntityFromCsv(file); switch(investmentEntity) { case "fleet": // 通过服务提供者获取实例,自动注入依赖 importService = _serviceProvider.GetRequiredService<ImportFleet>(); break; } if (importService != null) { importService.Import(file, UserId); } } } }
注册服务
在Program.cs(或Startup.cs)中把ImportFleet注册为DI服务:
builder.Services.AddScoped<ImportFleet>();
方法2:用工厂模式封装实例创建
把运行时实例化的逻辑封装到工厂类中,让InvestmentService只依赖工厂接口,降低耦合:
1. 定义工厂接口
namespace WebApi.Services.Import.Investments { public interface IImportFactory { IImport CreateImportService(string investmentEntity); } }
2. 实现工厂类
namespace WebApi.Services.Import.Investments { public class ImportFactory : IImportFactory { private readonly IServiceProvider _serviceProvider; public ImportFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IImport CreateImportService(string investmentEntity) { return investmentEntity switch { "fleet" => _serviceProvider.GetRequiredService<ImportFleet>(), // 后续新增子类时在这里添加case _ => null }; } } }
3. 注册工厂和导入类
builder.Services.AddScoped<IImportFactory, ImportFactory>(); builder.Services.AddScoped<ImportFleet>();
4. 在InvestmentService中使用工厂
namespace WebApi.Services { public interface IInvestmentService { public void Import(IFormFile file, int UserId, int InvestmentEntityId); } public class InvestmentService: IInvestmentService { private readonly IImportFactory _importFactory; public InvestmentService(IImportFactory importFactory) { _importFactory = importFactory; } public void Import(IFormFile file, int UserId, int InvestmentEntityId) { string investmentEntity = ImportBase.determineInvestmentEntityFromCsv(file); var importService = _importFactory.CreateImportService(investmentEntity); if (importService != null) { importService.Import(file, UserId); } } } }
方法3:反射自动匹配(进阶)
如果后续会添加大量导入子类,可以用反射自动扫描所有IImport实现类,结合特性标记实现自动匹配,避免每次修改工厂逻辑:
1. 定义特性类
[AttributeUsage(AttributeTargets.Class)] public class InvestmentEntityAttribute : Attribute { public string EntityName { get; } public InvestmentEntityAttribute(string entityName) { EntityName = entityName; } }
2. 给导入类标记特性
[InvestmentEntity("fleet")] public class ImportFleet : ImportBase { // 原有代码不变 }
3. 实现自动匹配的工厂
namespace WebApi.Services.Import.Investments { public class ImportFactory : IImportFactory { private readonly IServiceProvider _serviceProvider; private readonly Dictionary<string, Type> _importTypeMap; public ImportFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; // 扫描程序集中所有带特性的IImport实现类,构建映射关系 _importTypeMap = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => typeof(IImport).IsAssignableFrom(t) && !t.IsAbstract) .Select(t => new { Type = t, Attribute = t.GetCustomAttribute<InvestmentEntityAttribute>() }) .Where(x => x.Attribute != null) .ToDictionary(x => x.Attribute.EntityName, x => x.Type); } public IImport CreateImportService(string investmentEntity) { if (_importTypeMap.TryGetValue(investmentEntity, out var type)) { return (IImport)_serviceProvider.GetRequiredService(type); } return null; } } }
这种方式下,后续新增导入子类只需要:
- 创建继承
ImportBase的类 - 标记
[InvestmentEntity("xxx")]特性 - 在DI中注册该类
不需要修改工厂的匹配逻辑,扩展性更强。
内容的提问来源于stack exchange,提问作者Musaffar Patel
相关产品推荐
相关产品推荐

