ASP MVC Core启动后生成动态控制器程序集,路由中间件无法处理请求
解决ASP.NET Core启动后生成动态控制器的路由识别问题
我之前碰到过类似的场景——ASP.NET Core MVC的路由系统默认是在应用启动阶段完成控制器扫描和路由构建的,所以启动后动态生成的程序集里的控制器,RouteMiddleware根本“看不到”。你提到的AddApplicationPart是启动时配置的方式,启动后要处理的话,得手动触发两个关键步骤:更新应用部件管理器,然后通知路由系统重新加载动作描述符。
下面是具体的实现步骤和代码示例:
核心思路
ASP.NET Core通过ApplicationPartManager来管理所有可扫描的程序集(包括控制器所在的程序集),而路由系统依赖ActionDescriptor集合来匹配请求。启动后新增动态程序集时,需要:
- 将动态程序集添加到
ApplicationPartManager中 - 通知系统
ActionDescriptor集合已变更,触发重新扫描和构建
具体实现
1. 注入必要的服务
首先,在处理动态程序集生成的类中,注入ApplicationPartManager和IActionDescriptorChangeProvider(这两个都是单例服务,可以直接通过DI获取):
using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.Logging; public class DynamicControllerService { private readonly ApplicationPartManager _partManager; private readonly IActionDescriptorChangeProvider _changeProvider; private readonly ILogger<DynamicControllerService> _logger; public DynamicControllerService(ApplicationPartManager partManager, IActionDescriptorChangeProvider changeProvider, ILogger<DynamicControllerService> logger) { _partManager = partManager; _changeProvider = changeProvider; _logger = logger; } }
2. 添加动态程序集并触发路由更新
当你生成完动态程序集后,执行以下代码:
public async Task AddDynamicAssembly(Assembly dynamicAssembly) { // 1. 将动态程序集添加为ApplicationPart var assemblyPart = new AssemblyPart(dynamicAssembly); // 避免重复添加同一程序集 if (!_partManager.ApplicationParts.Any(p => p is AssemblyPart ap && ap.Assembly == dynamicAssembly)) { _partManager.ApplicationParts.Add(assemblyPart); _logger.LogInformation("已添加动态程序集: {AssemblyName}", dynamicAssembly.FullName); } // 2. 通知系统更新ActionDescriptor集合 if (_changeProvider is CompositeActionDescriptorChangeProvider compositeProvider) { // 清除现有变更令牌注册,标记为脏数据触发更新 compositeProvider.GetChangeTokenRegistrations().Clear(); compositeProvider.MarkDirty(); _logger.LogInformation("已触发路由系统更新,新控制器将被识别"); } }
关键细节说明
- 动态程序集生命周期:要确保动态生成的程序集不会被GC回收,建议将其保存在一个静态集合或者单例服务的字段中,避免后续请求时出现找不到程序集的问题。
CompositeActionDescriptorChangeProvider:这是ASP.NET Core默认的IActionDescriptorChangeProvider实现,调用MarkDirty()会让系统重新扫描所有ApplicationPart中的控制器,生成新的ActionDescriptor集合,路由中间件会自动使用这个新集合匹配请求。- 重复添加的问题:代码中加入了重复检查,避免同一个动态程序集被多次添加导致重复路由条目。
验证方式
添加完动态程序集并触发更新后,可以通过访问控制器的路由地址来验证,比如如果动态控制器有[Route("api/dynamic/[controller]")]特性,就可以请求/api/dynamic/YourDynamicController来测试是否能正常响应。
内容的提问来源于stack exchange,提问作者Maxim
相关产品推荐
相关产品推荐

