关于 dotnet-project-analysis
面向 .NET 开发 Agent 的项目结构分析技能,指导检查解决方案、项目依赖和构建配置,并定位关键入口文件。
主要功能
- 01发现解决方案与项目文件
- 02识别项目类型及测试项目
- 03构建项目依赖关系图
- 04检查共享构建与包配置
- 05定位项目入口及关键文件
适用场景
- 01接手陌生 .NET 仓库时梳理结构
- 02排查项目引用和构建配置问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-project-analysis安装后在 Claude Code 中输入/dotnet-project-analysis确认已加载。
详细介绍
dotnet-project-analysis
分析 .NET 解决方案结构、项目引用和生成配置。这是基础技能——在进行任何有意义的 .NET 开发工作之前,Agent 需要先了解项目布局。
**先决条件:**先运行 [skill:dotnet-version-detection],确定 TFM 和 SDK 版本。对于没有 .csproj 的 .NET 10+ 单文件应用,请改用 [skill:dotnet-file-based-apps]。
步骤 1:查找解决方案根目录
在工作区中查找解决方案文件,从当前目录开始,逐级向上查找至仓库根目录。
.sln(旧格式)
传统的 MSBuild 解决方案格式,包含项目路径和生成配置。
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp", "src\MyApp\MyApp.csproj", "{GUID}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp.Tests", "tests\MyApp.Tests\MyApp.Tests.csproj", "{GUID}"
EndProject
从 Project("...") 行中提取项目条目。第二个带引号的值是项目名称,第三个是 .csproj 的相对路径。
.slnx(现代 XML 格式)
基于 XML 的新解决方案格式(.NET 10+ SDK、Visual Studio 17.13+ 支持)。新项目优先使用此格式。
<Solution>
<Folder Name="/src/">
<Project Path="src/MyApp/MyApp.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/MyApp.Tests/MyApp.Tests.csproj" />
</Folder>
</Solution>
从 <Project Path="..." /> 元素中提取项目条目。解决方案文件夹(<Folder>)表示逻辑分组。
未找到解决方案文件
如果没有找到 .sln 或 .slnx,则递归扫描 .csproj 文件。报告:“未找到解决方案文件。发现 N 个项目文件。请考虑使用 dotnet new sln 和 dotnet sln add 创建解决方案。”
步骤 2:分析每个项目
对于步骤 1 中发现的每个 .csproj,读取其内容并提取以下信息。
项目 SDK 和类型
<Project Sdk="..."> 属性用于标识项目类型:
| SDK | 项目类型 | 描述 |
|---|---|---|
Microsoft.NET.Sdk | 类库 / 控制台 | 默认 SDK,检查是否存在 <OutputType> |
Microsoft.NET.Sdk.Web | Web(API / MVC / Razor Pages) | ASP.NET Core Web 应用程序 |
Microsoft.NET.Sdk.BlazorWebAssembly | Blazor WASM | 客户端 Blazor(旧版 SDK) |
Microsoft.NET.Sdk.Worker | Worker Service | 后台服务 / 守护进程 |
Microsoft.NET.Sdk.Razor | Razor 类库 | 共享 Razor 组件 |
Microsoft.Maui.Sdk 或包含 -android/-ios 的 TFM | MAUI | 跨平台移动 / 桌面应用 |
自定义 SDK 或 Uno.Sdk | Uno Platform | 跨平台 UI(检查是否引用 Uno) |
输出类型检测
如果 SDK 是 Microsoft.NET.Sdk,检查 <OutputType> 以区分:
| OutputType | 含义 |
|---|---|
Exe | 控制台应用程序 |
Library(或未设置) | 类库 |
WinExe | Windows 桌面应用程序(WPF/WinForms/WinUI) |
测试项目检测
满足以下任一条件,即视为测试项目:
- 设置了
<IsTestProject>true</IsTestProject> - 包含对
xunit.v3、xunit、NUnit、MSTest.TestFramework或Microsoft.NET.Test.Sdk的 PackageReference - 项目名称以
.Tests、.UnitTests、.IntegrationTests或.TestUtils结尾
Blazor 项目检测
满足以下任一条件,即视为 Blazor 项目:
- SDK 是
Microsoft.NET.Sdk.BlazorWebAssembly - 包含
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" /> - 项目目录中有
.razor文件 - 在启动代码中使用了
AddInteractiveServerComponents()或AddInteractiveWebAssemblyComponents()
MAUI 项目检测
满足以下任一条件,即视为 MAUI 项目:
- 设置了
<UseMaui>true</UseMaui> - SDK 是
Microsoft.Maui.Sdk - TFM 包含特定平台的目标框架:
net*-android、net*-ios、net*-maccatalyst、net*-windows(例如net8.0-android、net10.0-ios)
Uno Platform 检测
满足以下任一条件,即视为 Uno Platform 项目:
- SDK 是
Uno.Sdk或Uno.Sdk.Private - 包含对
Uno.WinUI或Uno.UI的 PackageReference - TFM 包含 Uno 专用目标框架(例如
net*-browserwasm、net*-desktop)
步骤 3:映射项目引用
读取每个 .csproj 中的 <ProjectReference> 元素,构建依赖关系图。
<ItemGroup>
<ProjectReference Include="..\MyApp.Core\MyApp.Core.csproj" />
<ProjectReference Include="..\MyApp.Infrastructure\MyApp.Infrastructure.csproj" />
</ItemGroup>
构建依赖关系图并报告:
Project Dependency Graph
========================
MyApp.Web (Web API)
-> MyApp.Core (Library)
-> MyApp.Infrastructure (Library)
-> MyApp.Core (Library)
MyApp.Tests (Test)
-> MyApp.Web (Web API)
-> MyApp.Core (Library)
标记问题:
- 循环引用:“检测到 Project A -> B -> A。这将导致构建失败。”
- 测试项目引用其他测试项目:“不常见——测试项目应引用生产代码,而不是其他测试项目。”
- 深层嵌套:超过 4 层可能表示过度抽象。
步骤 4:检测集中式构建配置
Directory.Build.props
从每个项目目录开始,向上搜索到解决方案根目录,查找 Directory.Build.props。这些文件会为其目录子树中的所有项目设置共享 MSBuild 属性。
需要报告的常见共享属性:
<TargetFramework>/<TargetFrameworks>——共享 TFM(参见 [skill:dotnet-version-detection])<LangVersion>——C# 语言版本<Nullable>enable</Nullable>——可空引用类型<ImplicitUsings>enable</ImplicitUsings>——隐式全局 using<TreatWarningsAsErrors>true</TreatWarningsAsErrors>——严格处理警告<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>——强制执行代码样式<AnalysisLevel>latest-all</AnalysisLevel>——分析器严重性级别<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>——CPM 指示标记
报告:“在 <path> 找到 Directory.Build.props。共享设置:[找到的属性列表]。这些设置适用于 <directory> 下的所有项目。”
Directory.Build.targets
以相同方式搜索 Directory.Build.targets。这些文件会在项目评估之后运行,通常包含:
- 共享
<PackageReference>项(例如,应用于所有项目的分析器) - 基于项目类型的条件逻辑
- 自定义 MSBuild 目标
报告:“在 <path> 找到 Directory.Build.targets。包含:[内容摘要]。”
多个 Directory.Build 文件
如果不同层级存在多个 Directory.Build.props 文件(例如,根目录和 src/),请报告层级关系:
Build Configuration Hierarchy
==============================
/repo/Directory.Build.props (root: Nullable, ImplicitUsings, LangVersion)
/repo/src/Directory.Build.props (src: TargetFramework, TreatWarningsAsErrors)
/repo/tests/Directory.Build.props (tests: IsTestProject, test-specific settings)
注意:内部文件不会自动导入外部文件。检查是否存在 <Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />,以确定是否配置了链接导入。
步骤 5:检测集中式包管理(CPM)
Directory.Packages.props
从解决方案根目录开始,向上搜索至仓库根目录(或文件系统根目录),查找 Directory.Packages.props。NuGet 会分层解析 CPM——单体仓库可能会在上级目录中包含 Directory.Packages.props,以管理多个解决方案。还要检查层级结构中的任何 Directory.Build.props 是否包含 <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>,因为 CPM 也可以在此处启用。
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.0" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
</ItemGroup>
</Project>
报告:
- 已启用 CPM:“集中式包管理已启用。包版本定义在
<path>的Directory.Packages.props中。各个.csproj文件使用不带Version属性的<PackageReference Include="..." />。” - 包数量:“集中管理 N 个包。”
- 版本覆盖:检查各个项目中的
<PackageReference ... VersionOverride="...">,并将其标记为例外。 - 继承的 CPM:如果
Directory.Packages.props位于解决方案根目录上级,请注明:“CPM 继承自<path>(位于解决方案根目录上级)。这在单体仓库中很常见。”
未使用 CPM
如果向上搜索未找到 Directory.Packages.props,且任何 Directory.Build.props 中都未设置 ManagePackageVersionsCentrally:
- 报告:“未配置集中式包管理。每个项目自行定义包版本。”
- 建议:“考虑启用 CPM,以保持版本一致。设置指南请参见 [skill:dotnet-project-structure]。”
步骤 6:检测其他配置文件
.editorconfig
检查解决方案根目录及嵌套目录中是否存在 .editorconfig。报告:
- 是否存在
- 关键规则:缩进样式/大小、命名约定、严重性覆盖
- .NET 专属节:
[*.cs]中的dotnet_style_*和csharp_style_*规则
nuget.config
从解决方案根目录开始,向上遍历父目录,搜索 nuget.config(不区分大小写)。NuGet 会按层级合并配置(项目 > 用户 > 计算机),因此多个文件可能共同影响最终配置。报告所有找到的文件及其内容:
- 配置的软件包源(例如 nuget.org、专用源、本地文件夹)
- 所有
<packageSourceMapping>条目(供应链安全最佳实践) - 所有
<disabledPackageSources>条目 - 注意:“用户级(
~/.nuget/NuGet/NuGet.Config)和计算机级配置也可能影响软件包解析。运行dotnet nuget list source查看合并后的有效软件包源。”
global.json
已由 [skill:dotnet-version-detection] 读取。在此报告相关详情:
sdk.version和sdk.rollForward策略- 如果存在,报告
msbuild-sdks节(自定义 SDK 版本)
.config/dotnet-tools.json
检查本地工具清单。报告已安装的工具:
dotnet-ef—— Entity Framework Core 工具dotnet-format—— 代码格式化工具nbgv—— Nerdbank.GitVersioning- 其他工具及其版本
第 7 步:识别入口点和关键文件
根据项目类型,引导 Agent 找到最重要的文件。
Web API / MVC / Razor Pages
- 入口点:
Program.cs(顶级语句、服务注册、中间件管道) - 配置:
appsettings.json、appsettings.{Environment}.json - 启动:查找
builder.Services注册和app.Map*端点定义 - 端点:
Program.cs中的 Minimal API 端点,或Endpoints/目录下*.cs文件中的端点;基于 Controller 的端点位于Controllers/目录
控制台应用程序
- 入口点:
Program.cs(顶级语句或static void Main) - 配置:如果使用
IHostBuilder/ Generic Host,则查找appsettings.json
Worker 服务
- 入口点:包含
builder.Services.AddHostedService<Worker>()的Program.cs - Worker:继承
BackgroundService并重写ExecuteAsync的类
类库
- 无入口点:记录公共 API 范围(公共类/接口)
- 关键文件:查找主要命名空间中的类型
Blazor
- 入口点:包含组件注册的
Program.cs - 根组件:
App.razor或Routes.razor - 布局:
Layout/或Shared/中的MainLayout.razor - 页面:包含
@page指令的.razor文件
MAUI
- 入口点:包含
CreateMauiApp()的MauiProgram.cs - 应用 Shell:用于导航结构的
AppShell.xaml - 页面:
Views/或Pages/目录下的文件 - 平台专属内容:
Platforms/目录下的 Android、iOS、Windows、Mac Catalyst 文件夹
测试项目
- 测试文件:带有
[Fact]、[Theory]、[Test]或[TestMethod]特性的*.cs文件 - Fixture:实现
IClassFixture<T>或ICollectionFixture<T>的类 - 配置:查找集成测试中
WebApplicationFactory<T>的用法
结构化输出格式
完成分析后,按以下格式呈现结果:
.NET Project Analysis Results
==============================
Solution: MyApp.slnx (or MyApp.sln)
Projects: 5 (2 libraries, 1 web API, 1 console, 1 test)
CPM: enabled (42 packages in Directory.Packages.props)
Shared Config: Directory.Build.props (Nullable, ImplicitUsings, LangVersion=14)
Code Style: .editorconfig present
Package Sources: nuget.org + private feed (packageSourceMapping configured)
Local Tools: dotnet-ef 10.0.0, nbgv 3.7.0
Project Dependency Graph
------------------------
MyApp.Api (Web API, net10.0) -> entry: src/MyApp.Api/Program.cs
-> MyApp.Core (Library)
-> MyApp.Infrastructure (Library)
-> MyApp.Core (Library)
MyApp.Console (Console, net10.0) -> entry: src/MyApp.Console/Program.cs
-> MyApp.Core (Library)
MyApp.Tests (Test, xUnit) -> entry: tests/MyApp.Tests/
-> MyApp.Api (Web API)
-> MyApp.Core (Library)
Key Files
---------
- Solution root: /repo/MyApp.slnx
- Shared props: /repo/Directory.Build.props
- Package versions: /repo/Directory.Packages.props
- API entry point: /repo/src/MyApp.Api/Program.cs
- API config: /repo/src/MyApp.Api/appsettings.json
边缘情况
混合解决方案格式
如果 .sln 和 .slnx 同时存在,优先使用 .slnx(现代格式)。注明:“同时找到 .sln 和 .slnx。使用 .slnx 作为主要解决方案文件。.sln 可能仍用于兼容较旧的工具。”
包含多个解决方案的 Monorepo
如果存在多个 .sln / .slnx 文件,报告所有文件,并询问用户要分析哪个解决方案。如果其中一个位于仓库根目录,则默认使用该文件。
未包含在解决方案中的项目
如果存在未被任何解决方案文件引用的 .csproj 文件,将其报告为孤立项目:“发现 N 个未包含在任何解决方案中的项目文件。这些项目可能是实验性的或未使用。”
条件项目引用
如果 <ProjectReference> 位于 <When> 内或带有 Condition 属性:
<ProjectReference Include="..\MyApp.DevTools\MyApp.DevTools.csproj"
Condition="'$(Configuration)' == 'Debug'" />
报告:“对 MyApp.DevTools 的条件引用(仅限 Debug)。”
缺少 launchSettings.json 的 Web 项目
如果 Web 项目没有 Properties/launchSettings.json,请注明:“未找到 launchSettings.json。该项目使用默认的 Kestrel 设置。建议添加用于开发的启动配置文件。”