如何在.NET 8.0项目的Startup.cs中获取所有ProjectReference并转为数组
如何在.NET 8.0项目的Startup.cs中获取所有ProjectReference并转为数组
当然可以搞定!不用再手动硬编码每个类库的程序集名称啦,我给你两种实用的方案,帮你自动获取所有项目引用的类库程序集,轻松注册里面的服务:
方案一:通过程序集依赖关系自动筛选
这种方案的核心思路是:我们自己的类库项目都会引用定义了IService的Common库,而第三方包不会。所以可以通过当前Web程序集的依赖项,筛选出符合这个条件的程序集,完全不用硬编码任何名称。
public static void ConfigureServices(this IServiceCollection services) { // 获取当前Web项目的程序集 var webAssembly = Assembly.GetExecutingAssembly(); // 获取定义了IService的Common程序集,作为筛选依据 var commonAssembly = typeof(IService).Assembly; // 筛选出所有引用了Common程序集的依赖程序集(即我们的项目类库) var referencedAssemblies = webAssembly.GetReferencedAssemblies() .Select(asmName => { try { return Assembly.Load(asmName); } catch { // 加载失败的跳过(比如第三方包的程序集) return null; } }) .Where(asm => asm != null && asm.GetReferencedAssemblies().Any(r => r.FullName == commonAssembly.FullName)) .Select(asm => asm!) // 把Web程序集和Common程序集也加进去 .Concat(new[] { webAssembly, commonAssembly }) .Distinct() .ToArray(); // 从所有筛选到的程序集中,找出所有IService的实现类 var serviceTypes = referencedAssemblies .SelectMany(asm => asm.GetTypes()) .Where(t => typeof(IService).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract) .ToArray(); // 批量注册服务 foreach (var serviceType in serviceTypes) { // 获取对应的服务接口(排除IService本身,取它的子接口) var interfaceType = serviceType.GetInterfaces() .FirstOrDefault(i => typeof(IService).IsAssignableFrom(i) && i != typeof(IService)); if (interfaceType != null) { services.AddScoped(interfaceType, serviceType); } } }
方案优势
- 完全自动适配项目引用的变化,新增/删除类库项目时,代码不需要任何修改
- 不会误把第三方包的程序集加进来,筛选逻辑精准
方案二:通过MSBuild提前收集项目引用
如果你的项目结构比较固定,也可以通过MSBuild在编译时自动收集所有ProjectReference的名称,然后在代码里读取这个列表,这种方式更直接可控。
第一步:修改Web项目的.csproj
在Web项目的.csproj文件里添加一个Target,编译时自动生成包含所有项目引用名称的文件:
<Project Sdk="Microsoft.NET.Sdk.Web"> <!-- 保留你原有的PropertyGroup、ItemGroup内容 --> <!-- 新增:编译时收集所有ProjectReference的名称,写入文件 --> <Target Name="CollectProjectReferences" BeforeTargets="Build"> <ItemGroup> <ProjectReferenceAssemblyNames Include="%(ProjectReference.Name)" /> </ItemGroup> <WriteLinesToFile File="$(OutDir)\ProjectReferences.txt" Lines="@(ProjectReferenceAssemblyNames)" Overwrite="true" Encoding="UTF-8" /> </Target> </Project>
第二步:在ConfigureServices中读取并加载程序集
public static void ConfigureServices(this IServiceCollection services) { // 读取编译时生成的项目引用名称文件 var referencesFilePath = Path.Combine(AppContext.BaseDirectory, "ProjectReferences.txt"); if (!File.Exists(referencesFilePath)) { throw new FileNotFoundException("未找到项目引用列表文件,请检查MSBuild配置是否正确", referencesFilePath); } var projectAssemblyNames = File.ReadAllLines(referencesFilePath) .Where(name => !string.IsNullOrWhiteSpace(name)) .ToArray(); // 加载所有项目类库程序集,加上Web程序集和Common程序集 var referencedAssemblies = projectAssemblyNames .Select(Assembly.Load) .Concat(new[] { Assembly.GetExecutingAssembly(), typeof(IService).Assembly }) .Distinct() .ToArray(); // 后续的服务注册逻辑和方案一完全一致 var serviceTypes = referencedAssemblies .SelectMany(asm => asm.GetTypes()) .Where(t => typeof(IService).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract) .ToArray(); foreach (var serviceType in serviceTypes) { var interfaceType = serviceType.GetInterfaces() .FirstOrDefault(i => typeof(IService).IsAssignableFrom(i) && i != typeof(IService)); if (interfaceType != null) { services.AddScoped(interfaceType, serviceType); } } }
方案优势
- 筛选逻辑100%精准,完全基于编译时的项目引用列表
- 不需要处理第三方包程序集的加载异常,性能更稳定
额外小提示
如果你的项目程序集有统一的命名规则(比如都以YourCompany.开头),还可以简化方案一中的筛选逻辑,直接根据程序集名称前缀判断:
// 替换方案一中的筛选逻辑 var referencedAssemblies = webAssembly.GetReferencedAssemblies() .Select(asmName => { try { return Assembly.Load(asmName); } catch { return null; } }) .Where(asm => asm != null && asm.Name.StartsWith("YourCompany.")) .Select(asm => asm!) .Concat(new[] { webAssembly, commonAssembly }) .Distinct() .ToArray();
备注:内容来源于stack exchange,提问作者bcExpt1123
相关产品推荐
相关产品推荐

