You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为运行时实例化的类实现依赖注入?

问题描述

我有两个类: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 06:25:50