
如果你最近在折腾 .NET 项目的智能化改造大概率会遇到一个尴尬场景大模型写代码很溜但让它理解一个几千个类的既有解决方案它经常答非所问。原因不难理解——大模型只能看到你手动粘贴的代码片段缺少对整个代码库结构的全局感知。我最近在 Hacker News 上看到有人开源了一个叫 Slnmap 的 .NET 工具它正好从这个痛点切入用 Roslyn 把 .NET 代码库解析成代码图再以 MCP Server 的方式暴露给 AI 客户端调用。这篇文章会围绕 Slnmap 做一个完整拆解内容包括它到底是什么、Roslyn 和 MCP Server 在其中各自承担什么职责、如何安装并配置到 MCP 客户端里、怎么用自然语言查询项目依赖和调用关系以及我在实际体验中整理的常见报错和工程建议。无论你是正在做 .NET 老项目重构还是想给自己团队的代码库接入 AI 辅助开发链路都值得往下看。1. 背景与核心概念1.1 Slnmap 到底是什么先给一个通俗解释Slnmap 是一个面向 .NET 解决方案的“代码地图生成器 查询服务”。它读取 .sln 解决方案、.csproj 项目文件和 C# 源码把项目之间的引用关系、类型之间的继承关系、方法之间的调用关系整理成一张结构化代码图。然后它通过 MCP Server 的形式对外提供查询接口让支持 MCP 协议的 AI 客户端可以通过自然语言去检索这张代码图。换句话说Slnmap 不像传统的静态分析工具那样只是生成几个 HTML 报告而是把代码库变成一种“可被大模型实时查询的数据服务”。这在 AI 辅助编程日益普及的背景下很有价值。与 .NET Framework 时代流行的各种代码分析工具相比Slnmap 更贴近现代 .NET 生态。它基于 Roslyn 工作区 API 打开解决方案这意味着它能够处理 SDK 风格的项目文件、跨平台构建配置以及 NuGet 引用而不是只停留在传统的 XML 项目文件解析层面。1.2 Roslyn.NET 编译器平台的底座要理解 Slnmap必须先理解 Roslyn。Roslyn 是微软开源的 .NET 编译器平台它不仅仅是“编译器”而是一整套公开的 API。传统编译器通常是一个黑盒源代码进去程序集出来中间过程不对外暴露。Roslyn 打破了这种设计它把编译过程拆成多个公开阶段每一个阶段都能被开发者读取和操作。核心对象包括SyntaxTree语法树描述源码的语法结构。一个类、一个方法、一条语句在语法树里都有对应节点。SemanticModel语义模型在语法树基础上做符号解析告诉你某个标识符对应的是哪个类型、哪个方法。ISymbol符号类型、方法、字段、属性、事件等元数据元素在 Roslyn 中的统一表示。MSBuildWorkspaceRoslyn 提供的工作区入口可以直接打开 .sln 或 .csproj并自动还原项目引用和 NuGet 包。Slnmap 的价值本质上是把 Roslyn 的这些底层能力“翻译”成 AI 更容易消费的服务。它不需要大模型直接理解 C# 语法树而是由 Roslyn 完成精确解析再由 MCP Server 输出结构化查询结果。1.3 MCP Server大模型连接外部世界的桥梁MCP 的全称是 Model Context Protocol翻译过来是“模型上下文协议”。它解决一个很实际的问题大模型训练时的知识是静态的无法实时知道你本地项目的真实结构。MCP 定义了一套统一的通信协议让 AI 客户端可以像“插 USB 设备”一样接入外部数据源或工具。在 MCP 的架构里有两个角色MCP Client通常是 AI 聊天客户端或 IDE 插件比如 Claude Desktop、Cursor、VS Code 的 AI 扩展等。MCP Server提供具体能力和数据的本地服务通过标准协议暴露给 Client。拿数据库驱动来类比AI 客户端相当于 JDBC 的调用方MCP Server 相当于数据库驱动而你的代码库就相当于数据库。Slnmap 是专门识别 .NET 代码库结构的“驱动”它把代码查询能力封装成一组可调用的 MCP 工具。1.4 为什么需要“代码图”而不是“直接贴源码”可能有人会问把源码直接丢给大模型不行吗在小项目中可以但一旦代码量上来问题就非常明显。第一大模型的上下文窗口有限。一个中等规模的 .NET 解决方案可能有几十个项目、上千个文件全部塞进去不现实。第二大模型对代码仓库“整体结构”的理解力很弱。你可以把 OrderController.cs 粘给它但它不知道 OrderController 依赖 OrderService也不知道 OrderService 被三个地方引用。第三精确的符号引用关系是语义层面的大模型靠“读文本”猜测往往会出现幻觉而 Roslyn 是编译器级的解析结果确定、可复现。Slnmap 的代码图其实就是把“结构信息”和“引用关系”做成了可查询的知识网络。AI 在回答问题时不是凭记忆瞎猜而是先通过 MCP 工具查一下真实代码库再基于查询结果回答。2. 环境准备与版本说明2.1 运行环境Slnmap 是基于 .NET 的工具因此运行环境首先需要安装 .NET SDK。从 Roslyn 工具链的现状来看现代 .NET SDK比如 .NET 6、.NET 8 或更高版本都是常见选择。具体哪个版本必须满足 Slnmap 的要求要以项目官方 README 为准。操作系统方面Windows、macOS、Linux 均可。Windows 环境需要注意路径分隔符和解决方案文件的编码macOS/Linux 环境需要注意 SDK 风格的 csproj 和跨平台路径问题。你本地的目标项目可以是 .NET Framework 项目也可以是现代 .NET 项目。但有一点要提醒如果目标项目是传统 .NET Framework 项目且依赖了大量 GAC 程序集MSBuildWorkspace 在非 Windows 环境下加载可能会受限。这是因为 Roslyn 需要调用 MSBuild 相关逻辑而 .NET Framework 项目的评估在跨平台环境中经常碰到兼容问题。2.2 安装 SlnmapSlnmap 的安装方式目前常见两种一种是通过 dotnet global tool 安装为命令行工具另一种是直接 clone 源码构建。由于项目还在快速迭代安装命令可能变化我这里给出通用的思路。如果项目已经发布为 dotnet global tool安装命令通常是dotnet tool install -g Slnmap安装后验证是否成功slnmap --version如果采用源码构建方式步骤一般是拉取仓库、还原依赖、发布或直接运行git clone 项目仓库地址 cd Slnmap dotnet restore dotnet run -- --solution /path/to/your.sln需要注意的是Slnmap和slnmap在大小写上可能有区别具体命令名以官方文档为准。如果你用源码构建输出路径里可能会生成一个slnmap可执行文件需要把它添加到 PATH 中。2.3 准备 MCP 客户端Slnmap 以 MCP Server 的方式运行需要一个 MCP Client 来连接它。常见的客户端有Claude DesktopCursorJetBrains IDE 的 AI 插件自研的 MCP Client 应用不同客户端的配置入口不一样。以 Claude Desktop 为例它通常会读取一个 JSON 配置文件在其中注册 MCP Server。下面是一个典型的配置片段注意不同客户端字段可能有差异请以客户端的官方文档为准{ mcpServers: { slnmap: { command: slnmap, args: [ --solution, D:\\projects\\MyApp\\MyApp.sln ] } } }在 Windows 路径中反斜杠需要写成\\否则 JSON 解析会报错。macOS 或 Linux 下路径可以直接写{ mcpServers: { slnmap: { command: slnmap, args: [ --solution, /home/user/projects/MyApp/MyApp.sln ] } } }2.4 验证是否连通配置完成后重启 MCP 客户端然后向 AI 发送一个简单问题比如“分析当前解决方案中所有项目的依赖关系”。如果客户端能返回项目之间引用关系说明 Slnmap 已经成功接入。如果客户端没有返回任何内容可以先在命令行手动运行一次 Slnmap 命令观察是否有报错信息slnmap --solution /path/to/your.sln这一步能帮助快速定位是工具本身的问题还是客户端配置的问题。3. 核心原理拆解Roslyn 代码分析3.1 用 MSBuildWorkspace 打开解决方案Slnmap 的第一步是加载解决方案。Roslyn 的 MSBuildWorkspace 可以读取 .sln 和 .csproj并自动处理项目之间的引用关系。下面是一个最小示例演示如何用 Roslyn 打开一个解决方案并遍历项目// 文件路径src/CodeGraphDemo/Program.cs using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.MSBuild; string solutionPath D:\projects\MyApp\MyApp.sln; using var workspace MSBuildWorkspace.Create(); var solution await workspace.OpenSolutionAsync(solutionPath); foreach (var project in solution.Projects) { Console.WriteLine($Project: {project.Name}); foreach (var projectReference in project.ProjectReferences) { Console.WriteLine($ - Reference: {projectReference.ProjectId}); } }这段代码会打印出解决方案里每个项目名称以及它引用的其他项目。这就是代码图中“项目依赖边”的最初来源。需要提醒的是要在你自己的控制台项目中使用这段代码需要添加 NuGet 包Microsoft.CodeAnalysis.Workspaces.MSBuild。版本要根据你的 .NET SDK 环境选择不同 Roslyn 版本之间可能存在兼容差异。3.2 获取编译对象与语义模型打开解决方案后接下来的关键步骤是获取每个项目的 Compilation编译对象。Compilation 是 Roslyn 的编译单元包含完整的语法树和符号信息。foreach (var project in solution.Projects) { var compilation await project.GetCompilationAsync(); if (compilation is null) continue; Console.WriteLine($Compilation: {compilation.AssemblyName}); foreach (var syntaxTree in compilation.SyntaxTrees) { var root await syntaxTree.GetRootAsync(); var model compilation.GetSemanticModel(syntaxTree); // 遍历所有方法声明 foreach (var methodDecl in root.DescendantNodes().OfTypeMethodDeclarationSyntax()) { var symbol model.GetDeclaredSymbol(methodDecl); Console.WriteLine($ Method: {symbol?.Name}); } } }注意MethodDeclarationSyntax属于Microsoft.CodeAnalysis.CSharp.Syntax命名空间代码里需要引入using Microsoft.CodeAnalysis.CSharp.Syntax;还需要添加Microsoft.CodeAnalysis.CSharp包。这里我使用的是root.DescendantNodes().OfTypeMethodDeclarationSyntax()它会找出当前语法树中所有方法声明节点。然后通过GetDeclaredSymbol获取这个声明对应的语义符号从而知道方法的名称、访问修饰符、所属类型、参数列表等完整信息。这个“从语法节点转换为语义符号”的过程就是 Roslyn 帮助开发者从“看代码文本”升级到“理解代码含义”的桥梁。3.3 代码图的节点与边Slnmap 输出的“代码图”本质上是一张有向图。节点代表代码实体边代表实体之间的关系。常见的节点类型包括Project项目Type类型Method方法Property属性Field字段常见的边类型包括ProjectReference项目引用TypeInherits类型继承MethodCalls方法调用PropertyUses属性使用FieldUses字段使用以方法调用关系为例Roslyn 可以在语法树中找到所有“调用表达式”然后通过语义模型解析出它真正调用的目标方法符号。Slnmap 把这些信息累积起来形成一张从“调用方”指向“被调用方”的有向边。后续 AI 查询“谁调用了 GetOrderById”时就直接在这张图上逆向搜索。这种设计的好处是图结构天然适合表达依赖关系查询路径清晰也便于做影响面分析。3.4 MCP Server 如何暴露这些能力代码图构建完成后Slnmap 需要把它暴露给 AI 客户端。这一步是 MCP Server 的职责。一般情况下MCP Server 会声明一组工具Tools每个工具对应一种查询能力。Slnmap 可能的工具包括list_solutions列出已加载的解决方案list_projects列出解决方案中的所有项目及依赖get_type_info查询某个类型的定义和成员find_callers查询某个方法被哪些地方调用get_project_dependencies查询项目依赖关系AI 客户端不是把问题直接翻译成 SQL 或 C# 代码而是识别出用户意图然后调用相应的 MCP 工具。例如用户问“OrderService 被哪些 Controller 使用”AI 可能会调用find_callers传入OrderService相关的方法名再基于返回结果组织语言回答。这种“意图识别 工具调用”的模式是目前 MCP 生态最常见的交互方式。Slnmap 提供的是代码库领域的“领域工具集”和其他 MCP Server 提供的数据库查询、搜索、文件操作等能力并不冲突可以同时挂载在同一个客户端里。4. 完整实战案例在本地项目中配置 Slnmap4.1 创建示例项目为了演示完整流程我们先准备一个最小的 .NET 解决方案。它包含两个项目一个核心类库和一个 API 项目。目录结构如下MyApp/ ├── MyApp.sln ├── MyApp.Core/ │ ├── MyApp.Core.csproj │ └── Services/ │ └── OrderService.cs └── MyApp.Web/ ├── MyApp.Web.csproj └── Controllers/ └── OrderController.csOrderService.cs是一个简单的服务类// 文件路径MyApp.Core/Services/OrderService.cs namespace MyApp.Core.Services; public class OrderService { public string GetOrderById(int orderId) { return $Order-{orderId}; } public string CreateOrder(string productName) { return $Order created for {productName}; } }OrderController.cs是 API 控制器依赖OrderService// 文件路径MyApp.Web/Controllers/OrderController.cs using Microsoft.AspNetCore.Mvc; using MyApp.Core.Services; namespace MyApp.Web.Controllers; [ApiController] [Route(api/[controller])] public class OrderController : ControllerBase { private readonly OrderService _orderService; public OrderController(OrderService orderService) { _orderService orderService; } [HttpGet({id})] public IActionResult Get(int id) { var result _orderService.GetOrderById(id); return Ok(result); } }MyApp.Web.csproj需要引用MyApp.Core.csprojProject SdkMicrosoft.NET.Sdk.Web PropertyGroup TargetFrameworknet8.0/TargetFramework /PropertyGroup ItemGroup ProjectReference Include..\MyApp.Core\MyApp.Core.csproj / /ItemGroup /Project这样一个简单的解决方案就准备好了。它虽然小但足够演示 Slnmap 的两类核心能力项目依赖查询和类型引用查询。4.2 使用 Slnmap 启动代码图服务假设你已经通过 dotnet global tool 或源码构建安装好了 Slnmap在命令行执行slnmap --solution MyApp.sln如果 Slnmap 是 MCP Server 模式它会以 stdio 方式启动等待 MCP Client 接收标准输入和输出。也就是说直接终端运行可能看不到明显输出需要配合 MCP Client 使用。你也可以在 MCP 客户端配置中直接指定解决方案路径。以 Claude Desktop 为例配置文件中的片段如下{ mcpServers: { slnmap: { command: slnmap, args: [ --solution, /home/user/projects/MyApp/MyApp.sln ] } } }配置完成后重启客户端在对话中发送问题即可。4.3 通过自然语言查询代码库下面是几个典型的查询场景以及 Slnmap 会如何支撑这些回答。场景一查看项目依赖关系用户提问“这个解决方案里的项目依赖关系是怎样的”Slnmap 通过get_project_dependencies或类似工具返回项目引用信息。AI 再根据结果整理出MyApp.Web - MyApp.Core也就是说Web 项目引用了 Core 项目。这个关系在人工看 csproj 时也能发现但在大型解决方案中项目数量多、依赖链复杂让 AI 直接看图会高效得多。场景二查询某个方法被谁调用用户提问“OrderService.GetOrderById 被哪些方法调用”Slnmap 在代码图中搜索指向OrderService.GetOrderById的调用边返回调用方信息OrderController.Get - OrderService.GetOrderById这是一个典型的“反向调用链”查询对重构影响评估特别有用。如果我要修改GetOrderById的签名可以先通过这个查询评估会影响到哪些 Controller 或页面。场景三查看类型定义用户提问“OrderService 类有哪些公开方法”Slnmap 从语义模型取得OrderService的成员符号返回OrderService - string GetOrderById(int orderId) - string CreateOrder(string productName)这种查询在做代码评审、新成员快速了解项目时非常实用。尤其是新人加入一个不熟悉的老项目与其翻几小时代码不如直接用 AI 辅助梳理结构。4.4 运行结果与预期当 MCP 客户端正确加载 Slnmap 后以上查询应该能得到结构化的结果而不是让 AI 凭空编造。这一点值得特别强调Slnmap 的结果来自 Roslyn 对真实源码的编译级分析有较高的准确性。如果客户端返回“我不知道”或者“未找到相关代码”优先检查以下三项Slnmap 是否真的成功打开了解决方案而不是报错退出。你提问中涉及的类名、方法名是否和目标代码完全一致。是否配置了错误的解决方案路径。5. 常见问题与排查思路5.1 常见问题速查表问题现象常见原因解决思路找不到 slnmap 命令dotnet global tool 未安装成功或 PATH 未生效重新安装检查 PATHOpenSolutionAsync 加载失败项目文件损坏、SDK 版本不匹配、缺少 NuGet 包在命令行用dotnet build先验证项目可正常构建MCP 客户端显示没有工具配置格式错误、命令路径错误手动运行命令验证检查 JSON 配置大型解决方案内存占用过高代码图一次性加载所有项目拆分子解决方案或使用过滤条件Roslyn 版本冲突项目依赖的 Roslyn 版本和 Slnmap 不一致统一 SDK 版本或隔离运行.NET Framework 项目加载失败跨平台MSBuild 无法评估传统项目在 Windows 上运行或升级到 SDK 风格项目5.2 逐一排查问题一找不到 slnmap 命令现象是执行slnmap --version报“command not found”。这种情况通常是 dotnet global tool 的安装位置不在 PATH 中。检查安装是否成功dotnet tool list -g如果列表里面有 Slnmap再确认 tools 目录是否在 PATH 中。Linux/macOS 下一般需要把~/.dotnet/tools加入 PATH。问题二OpenSolutionAsync 加载失败Roslyn 打开解决方案依赖 MSBuild 评估。如果你的解决方案本身无法通过dotnet build构建那么 Slnmap 大概率也无法加载。排查顺序是在命令行执行dotnet build MyApp.sln。观察是否存在项目引用错误、NuGet 还原失败等问题。修复构建后重新启动 Slnmap。问题三MCP 客户端没有任何工具先确认 MCP Client 的配置文件是否被正确读取。很多客户端需要重启后才加载新的 MCP Server。如果重启后仍无效在命令行手动执行slnmap --solution /path/to/MyApp.sln如果这个命令能够正常启动且不立即退出说明工具本身没问题问题大概率出在配置格式上。检查 command 和 args 字段是否使用了绝对路径Windows 路径是否有转义。问题四内存与性能大型解决方案可能有几千个项目全部加载到 Roslyn 工作区会占用大量内存。Slnmap 如果支持过滤参数可以指定只加载某个子项目集合。例如slnmap --solution MyApp.sln --project MyApp.Core这里的--project参数是示意写法具体参数名以官方文档为准。但思路是明确的尽量缩小分析范围只加载当前任务关心的部分。6. 最佳实践与工程建议6.1 让代码库更适合被 AI 分析代码图再好也架不住代码结构混乱。为了让 Slnmap 和类似工具发挥更大价值建议从代码库层面做一些长期优化项目职责要清晰避免“上帝工程”被几十个项目同时引用。尽量消除或减少循环引用。循环依赖会让代码图出现环路干扰影响面分析。避免把生成代码、第三方源码直接放进解决方案或者在分析时排除生成目录。这不只是为了 Slnmap而是为了整个代码库的可维护性。AI 工具只是把原本就存在的结构问题更早、更清晰地暴露出来。6.2 控制代码图的分析范围不是所有时候都需要加载整个解决方案。日常开发中你可能只关心当前功能模块涉及的三四个项目。建议在需要做局部分析时只把相关项目纳入 Slnmap 范围只有在做全库重构评估时才加载完整解决方案。这样做的好处一是节省内存二是提升查询响应速度三是避免大模型的注意力被无关代码干扰。6.3 结合 CI/CD 做代码图快照Slnmap 不仅可以在本地交互式运行也可以集成到 CI 流程中。比如在每次 PR 合并后自动生成代码图快照并计算关键指标新增了多少方法、产生了多少新的跨项目引用、是否存在循环依赖等。这种能力对架构治理很有帮助。当项目还在成长阶段时人工审查可能看不出依赖恶化但代码图能直观呈现出依赖走向。6.4 安全与合规红线使用 MCP Server 把代码库暴露给 AI 客户端需要注意安全边界尽量在本地运行 MCP Server不要轻易通过公网端口暴露。如果使用云端大模型服务确保发送到模型的代码内容符合公司信息安全要求。对私有代码库、涉及商业秘密的模块应谨慎开启全库分析。不要把 MCP Server 配置成“允许模型修改代码”的模式除非你对权限控制有充分把握。还有一个容易被忽略的点MCP 工具的查询结果可能被记录在 AI 服务商的服务端日志中。对于敏感项目请先确认数据合规政策。6.5 从 Slnmap 出发的学习路线Slnmap 本质上是一个很好的 Roslyn 实践案例。如果你对它背后的原理感兴趣可以从以下几个方向继续学习Roslyn API 系统学习官方文档有很多语法分析、语义分析的示例掌握后你也能自己写代码分析工具。MCP 协议理解去看 MCP 官方协议了解 Tool、Resource、Prompt 三种能力模型。代码图建模学习图数据库比如 Neo4j和依赖分析算法可以把 Slnmap 生成的图导入更丰富的分析平台。AI Agent 与 IDE 集成探索把 Slnmap 接入 Cursor、Continue、自研 IDE 插件等场景。如果只是想用 Slnmap 解决实际问题掌握安装配置和基本查询就足够如果想让自己的工具链更智能Roslyn MCP 这套组合非常值得深入研究。7. 总结这篇教程系统介绍了 Slnmap 这个基于 Roslyn 的 .NET 代码图 MCP Server重点讲了三个核心问题它是什么、为什么有用、怎么配置使用。可以带走的几个关键信息是Slnmap 用 Roslyn 对真实源码做编译级分析把项目依赖、类型继承、方法调用等关系构造成代码图它通过 MCP Server 把代码图能力开放给 AI 客户端让大模型基于事实回答代码库问题在大型项目重构、代码审查、新人上手和 AI 辅助开发这几个场景中它都能明显提升效率。实际落地时优先确认你的解决方案能被dotnet build正常构建再把 Slnmap 接到 MCP 客户端里从一个小模块开始试用。遇到报错时按照“命令行手动运行 → 检查配置 → 验证 SDK 版本”的顺序排查基本能解决大部分问题。接下来建议你亲自搭一个最小 .NET 解决方案安装 Slnmap 试几次查询。跑通之后再试着把它接入到你真实项目的某个子模块。只有真正在项目里用起来你才会感受到代码图 MCP 这套方案对 AI 辅助开发体验的提升有多大。