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

.NET 6中如何为反射加载的模块实现依赖注入

问题描述

我正尝试将若干程序集加载到主应用中,这些程序集需要使用主应用中已配置依赖注入(DI)的DbContext。主应用中通过以下代码从bin目录获取对应程序集:

var rulesList = Directory
    .GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
    .Select(x => System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(x))
    .SelectMany(a => a
        .GetTypes()
        .Where(p =>
            p.IsClass &&
            p.IsAssignableTo(typeof(IMyRuleService))
        )
        .Select(Activator.CreateInstance)
        .Cast<IMyRuleService>()
    )
    .ToList();

这段代码能正常加载程序集,但我不清楚如何将DbContext注入到这些程序集中。由于无法使用带参数的构造函数,我考虑过方法注入,但可能存在需注入DbContext数量不固定的问题,或许可以通过方法注入传入所有可用DbContext的数组,但不确定这是否是合理方案。

补充被调用程序集的代码:

public class RuleService : IRedFlagRuleService
{
        private readonly MyDbContext _myDbContext ;    
        public RuleService(MyDbContext myDbContext)
        {
            _myDbContext = myDbContext; 
        }
                
        public Rule GetRuleInfo()
        {
            var ruleAssembly = System.Reflection.Assembly.GetExecutingAssembly();
            var rule = _myDbContext.Rules.Where(a => a.AssemblyName == ruleAssembly.GetName().Name).FirstOrDefault() ?? new Rule();    
            return rule;
        }
}
解决方案

1. 利用DI容器自动创建实例(推荐)

不要手动用Activator.CreateInstance,而是把扫描到的服务类型注册到DI容器,让容器负责解析构造函数依赖(包括DbContext),再从容器中获取实例。

步骤示例(以ASP.NET Core为例):

  1. 扫描程序集并注册服务类型:
// 扫描bin目录下的程序集
var assemblies = Directory
    .GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
    .Select(x => AssemblyLoadContext.Default.LoadFromAssemblyPath(x));

// 遍历所有程序集,找到IMyRuleService的实现类并注册到DI
foreach (var assembly in assemblies)
{
    var ruleTypes = assembly.GetTypes()
        .Where(p => p.IsClass && p.IsAssignableTo(typeof(IMyRuleService)));
    
    foreach (var type in ruleTypes)
    {
        // 根据需求选择生命周期:AddScoped/AddTransient/AddSingleton
        services.AddScoped(typeof(IMyRuleService), type);
    }
}
  1. 在需要使用的地方,通过IServiceProvider获取所有实例:
// 注入IServiceProvider或者在构造函数中接收IEnumerable<IMyRuleService>
var rulesList = serviceProvider.GetServices<IMyRuleService>().ToList();

这种方式完全利用DI容器的能力,自动处理构造函数中的DbContext注入,无需手动管理依赖。

2. 手动解析构造函数依赖创建实例

如果不想批量注册到DI容器,可以在找到类型后,用DI容器解析构造函数需要的参数,再调用带参构造创建实例:

// 假设已经获取到IServiceProvider实例
var serviceProvider = ...;

var rulesList = Directory
    .GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
    .Select(x => AssemblyLoadContext.Default.LoadFromAssemblyPath(x))
    .SelectMany(a => a
        .GetTypes()
        .Where(p => p.IsClass && p.IsAssignableTo(typeof(IMyRuleService)))
        .Select(type => 
        {
            // 获取类型的构造函数(这里假设只有一个带DbContext的构造)
            var constructor = type.GetConstructor(new[] { typeof(MyDbContext) });
            if (constructor == null)
            {
                // 处理无匹配构造的情况,比如抛出异常或返回null
                return null;
            }
            // 从DI容器获取DbContext实例
            var dbContext = serviceProvider.GetRequiredService<MyDbContext>();
            // 创建实例并转换为IMyRuleService
            return (IMyRuleService)constructor.Invoke(new[] { dbContext });
        })
        .Where(service => service != null)
    )
    .ToList();

这种方式适合不需要长期注册服务,仅临时创建实例的场景,但需要自己处理构造函数的匹配逻辑。

3. 方法注入(备选方案)

如果必须使用无参构造创建实例,可以在IMyRuleService接口中定义初始化方法,创建实例后手动传入DbContext:

首先修改接口:

public interface IMyRuleService
{
    void Initialize(MyDbContext dbContext);
    // 其他原有方法
}

然后在实现类中实现该方法:

public class RuleService : IRedFlagRuleService
{
    private MyDbContext _myDbContext;    

    // 保留无参构造
    public RuleService()
    {
    }

    public void Initialize(MyDbContext dbContext)
    {
        _myDbContext = dbContext;
    }
                
    public Rule GetRuleInfo()
    {
        if (_myDbContext == null)
        {
            throw new InvalidOperationException("未初始化DbContext");
        }
        var ruleAssembly = Assembly.GetExecutingAssembly();
        var rule = _myDbContext.Rules.Where(a => a.AssemblyName == ruleAssembly.GetName().Name).FirstOrDefault() ?? new Rule();    
        return rule;
    }
}

最后在主应用中创建实例后调用初始化方法:

var dbContext = serviceProvider.GetRequiredService<MyDbContext>();

var rulesList = Directory
    .GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
    .Select(x => AssemblyLoadContext.Default.LoadFromAssemblyPath(x))
    .SelectMany(a => a
        .GetTypes()
        .Where(p => p.IsClass && p.IsAssignableTo(typeof(IMyRuleService)))
        .Select(type => 
        {
            var service = (IMyRuleService)Activator.CreateInstance(type);
            service.Initialize(dbContext);
            return service;
        })
    )
    .ToList();

如果有多个DbContext,可以修改Initialize方法接收IEnumerable<DbContext>或者多个具体的DbContext类型,根据实际需求调整。

内容的提问来源于stack exchange,提问作者John S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 10:30:37