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

ASP.NET Core 6运行时注册DI组件遇异常,求解决方案

解决ASP.NET Core运行时动态注册DbContext和仓储的问题

你遇到的Cannot modify ServiceCollection after application is built.异常,核心原因是ASP.NET Core在应用启动完成(执行builder.Build()生成ServiceProvider后),原始的ServiceCollection会被锁定,无法再添加或修改服务注册。直接通过保存的ServiceCollection实例去新增服务,必然触发这个错误。

下面是几种可行的解决方案:

方案一:使用动态服务工厂模式

不直接将DbContext和仓储注册到原生DI容器,而是自定义一个工厂类,负责在运行时加载程序集后创建对应的服务实例。控制器依赖这个工厂,而非具体的服务类型。

实现步骤:

  1. 定义服务工厂接口:
public interface IDynamicServiceFactory
{
    T GetService<T>() where T : class;
    object GetService(Type serviceType);
    void RegisterService(Type serviceType, Func<object> factory);
}
  1. 实现工厂类(单例注册):
public class DynamicServiceFactory : IDynamicServiceFactory
{
    private readonly ConcurrentDictionary<Type, Func<object>> _serviceFactories = new();
    private readonly IServiceProvider _rootProvider;

    public DynamicServiceFactory(IServiceProvider rootProvider)
    {
        _rootProvider = rootProvider;
    }

    public T GetService<T>() where T : class
    {
        return (T)GetService(typeof(T));
    }

    public object GetService(Type serviceType)
    {
        if (_serviceFactories.TryGetValue(serviceType, out var factory))
        {
            return factory();
        }
        //  fallback到原生DI
        return _rootProvider.GetService(serviceType);
    }

    public void RegisterService(Type serviceType, Func<object> factory)
    {
        _serviceFactories.AddOrUpdate(serviceType, factory, (_, __) => factory);
    }
}
  1. 启动时注册工厂:
builder.Services.AddSingleton<IDynamicServiceFactory, DynamicServiceFactory>();
  1. 修改你的Install接口,通过工厂注册服务:
[HttpGet("install")]
public async Task<bool> Install(IDynamicServiceFactory factory)
{       
    var assembly = ... // 加载外部程序集

    var implementationTypes = assembly.GetTypes()
        .Where(x => !x.IsInterface && !x.IsAbstract && typeof(IService).IsAssignableFrom(x));

    foreach (var implementationType in implementationTypes)
    {
        // 注册创建实例的委托,处理构造函数依赖(从原生DI获取)
        factory.RegisterService(implementationType, () => 
        {
            var ctor = implementationType.GetConstructors().First();
            var parameters = ctor.GetParameters()
                .Select(p => _rootProvider.GetService(p.ParameterType))
                .ToArray();
            return Activator.CreateInstance(implementationType, parameters);
        });
    }

    // 注册控制器(你已实现的逻辑)
    ...

    return true;
}
  1. 控制器中通过工厂获取服务:
public class MyController : ControllerBase
{
    private readonly IDynamicServiceFactory _factory;

    public MyController(IDynamicServiceFactory factory)
    {
        _factory = factory;
    }

    [HttpGet]
    public IActionResult Get()
    {
        var myRepository = _factory.GetService<IMyRepository>();
        // 使用仓储逻辑
        return Ok();
    }
}

方案二:替换为支持运行时更新的DI容器

比如使用Autofac作为ASP.NET Core的DI容器,它支持运行时更新容器注册。

实现步骤:

  1. 安装Autofac相关包:
Install-Package Autofac
Install-Package Autofac.Extensions.DependencyInjection
  1. 启动时配置Autofac:
var builder = WebApplication.CreateBuilder(args);
// 替换默认DI为Autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
    // 注册现有基础服务
    containerBuilder.RegisterType<SomeService>().As<ISomeService>();
});
  1. 保存Autofac容器实例用于运行时更新:
public class AutofacContainerProvider
{
    public IContainer Container { get; }

    public AutofacContainerProvider(IContainer container)
    {
        Container = container;
    }
}

// 在应用启动完成后注册
var app = builder.Build();
app.Services.AddSingleton(new AutofacContainerProvider(app.Services.GetRequiredService<IContainer>()));
  1. 在Install接口中更新Autofac容器:
[HttpGet("install")]
public async Task<bool> Install(AutofacContainerProvider containerProvider)
{       
    var assembly = ... // 加载外部程序集

    var implementationTypes = assembly.GetTypes()
        .Where(x => !x.IsInterface && !x.IsAbstract && typeof(IService).IsAssignableFrom(x));

    // 基于现有容器创建新构建器
    var builder = new ContainerBuilder();
    foreach (var implementationType in implementationTypes)
    {
        builder.RegisterType(implementationType).AsSelf().InstancePerLifetimeScope();
        // 注册服务对应的接口(排除标记接口IService)
        var interfaces = implementationType.GetInterfaces().Where(i => i != typeof(IService));
        foreach (var iface in interfaces)
        {
            builder.RegisterType(implementationType).As(iface).InstancePerLifetimeScope();
        }
    }

    // 更新现有容器
    builder.Update(containerProvider.Container);

    // 注册控制器(你已实现的逻辑)
    ...

    return true;
}

注意事项

  • 运行时注册服务要考虑线程安全,多请求调用Install接口时需加锁处理。
  • DbContext注册要确保能正确获取连接字符串等配置信息。
  • Scoped服务使用工厂模式时,要注意实例生命周期管理,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:55:25