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

.NET 6 Blazor Server应用运行时加载DLL后如何注册新REST API端点?

无需重启服务器加载Blazor Server动态API端点的实现方案

核心原理

ASP.NET Core默认在启动阶段扫描程序集并注册控制器路由,动态加载DLL后需手动触发控制器特性重新发现和路由端点刷新,才能让新API端点生效。

具体实现步骤

1. 自定义控制器特性提供器

创建类实现IControllerFeatureProvider,用于识别动态程序集中的控制器类型:

public class DynamicControllerFeatureProvider : IControllerFeatureProvider
{
    private readonly IEnumerable<Assembly> _dynamicAssemblies;

    public DynamicControllerFeatureProvider(IEnumerable<Assembly> dynamicAssemblies)
    {
        _dynamicAssemblies = dynamicAssemblies;
    }

    public bool IsController(TypeInfo typeInfo)
    {
        return typeInfo.IsClass 
               && !typeInfo.IsAbstract 
               && !typeInfo.IsGenericType 
               && (typeof(ControllerBase).IsAssignableFrom(typeInfo) 
                   || typeInfo.GetCustomAttribute<ApiControllerAttribute>() != null);
    }
}

2. 动态更新控制器注册与路由

加载完DLL后,通过ApplicationPartManager和EndpointDataSource刷新控制器和路由:

public async Task RefreshDynamicControllers(IEnumerable<Assembly> newAssemblies, IServiceProvider serviceProvider)
{
    var partManager = serviceProvider.GetRequiredService<ApplicationPartManager>();

    // 清理旧的动态控制器提供器
    var oldProvider = partManager.FeatureProviders.OfType<DynamicControllerFeatureProvider>().FirstOrDefault();
    if (oldProvider != null)
    {
        partManager.FeatureProviders.Remove(oldProvider);
    }

    // 添加新动态程序集到应用部件
    foreach (var assembly in newAssemblies)
    {
        partManager.ApplicationParts.Add(new AssemblyPart(assembly));
    }

    // 注册新的控制器识别规则
    partManager.FeatureProviders.Add(new DynamicControllerFeatureProvider(newAssemblies));

    // 重新构建控制器特性集合
    var controllerFeature = new ControllerFeature();
    await partManager.PopulateFeatureAsync(controllerFeature);

    // 注册动态控制器到服务容器
    var serviceCollection = serviceProvider.GetRequiredService<IServiceCollection>();
    foreach (var controllerType in controllerFeature.Controllers)
    {
        if (!serviceCollection.Any(sd => sd.ServiceType == controllerType))
        {
            serviceCollection.AddTransient(controllerType);
        }
    }

    // 刷新端点路由
    var dynamicDataSource = serviceProvider.GetRequiredService<EndpointDataSource>() as DynamicEndpointDataSource;
    dynamicDataSource?.Refresh();
}

3. 配置动态端点数据源

在Program.cs中替换默认端点数据源,支持动态刷新:

var builder = WebApplication.CreateBuilder(args);

// 添加控制器服务
builder.Services.AddControllers()
    .ConfigureApplicationPartManager(apm =>
    {
        // 可预先添加基础程序集
    });

// 注册动态端点数据源
builder.Services.AddSingleton<EndpointDataSource, DynamicEndpointDataSource>();

var app = builder.Build();

app.MapControllers();
app.Run();

4. 处理动态控制器的依赖注入

动态控制器的依赖项需注册到主服务容器,可通过扫描DLL中的服务类型实现:

public void RegisterDynamicServices(Assembly assembly, IServiceCollection serviceCollection)
{
    // 扫描DLL中带自定义[Injectable]特性的服务类
    var serviceTypes = assembly.GetTypes()
        .Where(t => t.IsClass && !t.IsAbstract && t.GetCustomAttribute<InjectableAttribute>() != null);

    foreach (var type in serviceTypes)
    {
        var interfaceType = type.GetInterfaces().FirstOrDefault(i => i.Name == $"I{type.Name}");
        if (interfaceType != null)
        {
            serviceCollection.AddTransient(interfaceType, type);
        }
        else
        {
            serviceCollection.AddTransient(type);
        }
    }
}

关键注意事项

  • 动态DLL需避免依赖版本冲突,建议使用相同的基础类库版本
  • 控制器路由模板需与现有路由无冲突,否则会导致匹配异常
  • 每次加载新DLL前,需清理旧的动态控制器注册,防止重复路由

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 05:35:10