ASP.NET Core动态加载多微服务程序集并获取所有API控制器
批量提取多微服务API程序集的控制器信息方案
你已经实现了单个程序集的控制器动作提取,现在要扩展到多微服务的场景,核心要解决程序集的批量加载和多程序集的控制器提取逻辑扩展这两个问题,下面是具体的实现步骤:
一、先搞定程序集的批量加载
首先你需要把所有微服务的API程序集(比如Order.API.dll、User.API.dll这类)统一存储到某个介质(数据库Blob、指定文件目录都可以),然后在需要提取控制器时动态加载这些程序集。这里要注意避免重复加载和依赖问题,推荐用AssemblyLoadContext来隔离加载:
// 示例:从指定目录加载所有API程序集 public static IEnumerable<Assembly> LoadApiAssemblies(string assemblyDirectory) { // 筛选所有API后缀的dll文件 var apiAssemblyPaths = Directory.GetFiles(assemblyDirectory, "*.API.dll", SearchOption.AllDirectories); // 创建可回收的加载上下文,避免内存泄漏 var loadContext = new AssemblyLoadContext(null, isCollectible: true); foreach (var path in apiAssemblyPaths) { try { using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); yield return loadContext.LoadFromStream(fileStream); } catch (Exception ex) { // 处理加载异常,比如记录日志 Console.WriteLine($"Failed to load assembly {path}: {ex.Message}"); } } }
如果是从数据库读取程序集,只需要把数据库中的二进制字节流读取出来,用loadContext.LoadFromStream加载即可,不用存临时文件。
二、扩展控制器提取逻辑到多程序集
基于你现有的代码,我们可以把逻辑扩展到多个程序集,同时优化判定条件(比如排除抽象控制器、基类自带的方法):
// 定义一个实体类存储控制器和动作信息 public class ControllerActionInfo { public string ControllerName { get; set; } public string ControllerFullName { get; set; } public List<string> ActionNames { get; set; } = new List<string>(); } public static List<ControllerActionInfo> ExtractAllControllers(IEnumerable<Assembly> apiAssemblies) { var controllerTypes = apiAssemblies .SelectMany(assembly => assembly.GetTypes()) // 精准判定控制器:排除抽象类,继承Controller或ControllerBase(ASP.NET Core的两种控制器基类) .Where(type => !type.IsAbstract && (typeof(Controller).IsAssignableFrom(type) || typeof(ControllerBase).IsAssignableFrom(type))) // 排除编译器自动生成的类型(比如视图代理类) .Where(type => !type.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any()); return controllerTypes .Select(type => new ControllerActionInfo { ControllerName = type.Name.Replace("Controller", ""), // 去掉默认的Controller后缀,更符合常规命名 ControllerFullName = type.FullName, ActionNames = type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) .Where(method => !method.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any()) // 排除Controller基类自带的方法(比如View、RedirectToAction这些) .Where(method => method.DeclaringType != typeof(Controller) && method.DeclaringType != typeof(ControllerBase)) .Select(method => method.Name) .ToList() }) .ToList(); }
三、整合流程:加载+提取+存储
最后把这两部分整合起来,完成从加载程序集到提取控制器信息,再存储到数据库或JSON的流程:
// 1. 加载所有API程序集(这里示例从本地目录加载,你可以换成从数据库读取) var apiAssemblies = LoadApiAssemblies(@"D:\Microservices\PublishedAPIs"); // 2. 提取所有控制器和动作信息 var allControllerInfo = ExtractAllControllers(apiAssemblies); // 3. 存储到JSON文件(或者序列化后存入数据库) var serializedResult = System.Text.Json.JsonSerializer.Serialize(allControllerInfo, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }); File.WriteAllText("AllControllers.json", serializedResult);
一些优化建议
- 缓存结果:如果微服务的API不会频繁更新,提取后可以把结果缓存起来,避免每次都重新加载程序集,提升性能
- 依赖处理:如果API程序集依赖其他类库,要确保这些依赖文件也和API程序集放在一起(或上传到同一存储介质),否则加载会失败
- 自定义过滤:如果API程序集中有非业务控制器(比如测试用的),可以通过命名空间或者自定义特性来过滤掉这些类型
内容的提问来源于stack exchange,提问作者felipe




