.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
相关产品推荐
相关产品推荐

