mcpField
D
dotnet-project-analysis

dotnet-project-analysis

github.com/wshaddix开发8679 次安装35 周前更新
代码仓库

关于 dotnet-project-analysis

面向 .NET 开发 Agent 的项目结构分析技能,指导检查解决方案、项目依赖和构建配置,并定位关键入口文件。

主要功能

  1. 01发现解决方案与项目文件
  2. 02识别项目类型及测试项目
  3. 03构建项目依赖关系图
  4. 04检查共享构建与包配置
  5. 05定位项目入口及关键文件

适用场景

  1. 01接手陌生 .NET 仓库时梳理结构
  2. 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.WebWeb(API / MVC / Razor Pages)ASP.NET Core Web 应用程序
Microsoft.NET.Sdk.BlazorWebAssemblyBlazor WASM客户端 Blazor(旧版 SDK)
Microsoft.NET.Sdk.WorkerWorker Service后台服务 / 守护进程
Microsoft.NET.Sdk.RazorRazor 类库共享 Razor 组件
Microsoft.Maui.Sdk 或包含 -android/-ios 的 TFMMAUI跨平台移动 / 桌面应用
自定义 SDK 或 Uno.SdkUno Platform跨平台 UI(检查是否引用 Uno)
输出类型检测

如果 SDK 是 Microsoft.NET.Sdk,检查 <OutputType> 以区分:

OutputType含义
Exe控制台应用程序
Library(或未设置)类库
WinExeWindows 桌面应用程序(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 设置。建议添加用于开发的启动配置文件。”

评论

支持 Markdown