用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/rudironsoni/Ghost --skill dotnet-project-structure命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | dotnet-project-structure |
| description | Sets up .NET solution layout. .slnx, Directory.Build.props, CPM, .editorconfig, analyzers. |
| allowed-tools | ["Read","Grep","Glob","Bash","Write","Edit"] |
Reference guide for modern .NET project structure and solution layout. Use when creating new solutions, reviewing existing structure, or recommending improvements.
Prerequisites: Run [skill:dotnet-version-detection] first to determine TFM and SDK version — this affects which features are available (e.g., .slnx requires .NET 9+ SDK).
Cross-references: [skill:dotnet-project-analysis] for analyzing existing projects, [skill:dotnet-scaffold-project] for generating a new project from scratch, [skill:dotnet-artifacts-output] for centralized build output layout (UseArtifactsOutput).
MyApp/
├── .editorconfig
├── .gitignore
├── global.json
├── nuget.config
├── Directory.Build.props
├── Directory.Build.targets
├── Directory.Packages.props
├── MyApp.slnx # .NET 9+ SDK / VS 17.13+
├── src/
│ ├── MyApp.Core/
│ │ └── MyApp.Core.csproj
│ ├── MyApp.Api/
│ │ ├── MyApp.Api.csproj
│ │ ├── Program.cs
│ │ └── appsettings.json
│ └── MyApp.Infrastructure/
│ └── MyApp.Infrastructure.csproj
└── tests/
├── MyApp.UnitTests/
│ └── MyApp.UnitTests.csproj
└── MyApp.IntegrationTests/
└── MyApp.IntegrationTests.csproj
Key principles:
src/ and tests/ directoriesThe XML-based solution format is human-readable and diff-friendly. Requires .NET 9+ SDK or Visual Studio 17.13+.
<Solution>
<Folder Name="/src/">
<Project Path="src/MyApp.Core/MyApp.Core.csproj" />
<Project Path="src/MyApp.Api/MyApp.Api.csproj" />
<Project Path="src/MyApp.Infrastructure/MyApp.Infrastructure.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/MyApp.UnitTests/MyApp.UnitTests.csproj" />
<Project Path="tests/MyApp.IntegrationTests/MyApp.IntegrationTests.csproj" />
</Folder>
</Solution>
Convert existing .sln to .slnx:
dotnet sln MyApp.sln migrate
The traditional format remains the fallback for older tooling, CI agents, and third-party integrations that don't support .slnx yet. Keep .sln alongside .slnx during the transition period if needed.
dotnet new sln -n MyApp
dotnet sln add src/**/*.csproj
dotnet sln add tests/**/*.csproj
Shared MSBuild properties applied to all projects in the directory subtree. Place at the repo root.
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>latest-all</AnalysisLevel>
</PropertyGroup>
</Project>
Inner files do not automatically import outer files. To chain them:
<!-- src/Directory.Build.props -->
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<!-- src-specific settings -->
</PropertyGroup>
</Project>
Common pattern: separate props for src vs tests:
repo/
├── Directory.Build.props # Shared: LangVersion, Nullable, ImplicitUsings
├── src/
│ └── Directory.Build.props # Imports parent + adds TreatWarningsAsErrors
└── tests/
└── Directory.Build.props # Imports parent + sets IsTestProject
Imported after project evaluation. Use for:
<Project>
<!-- Apply analyzers to all projects -->
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" PrivateAssets="all" />
</ItemGroup>
</Project>
CPM centralizes all NuGet package versions in Directory.Packages.props at the repo root. Individual .csproj files reference packages without a Version attribute.
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<!-- Shared dependencies -->
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.0" />
<PackageVersion Include="System.Text.Json" Version="10.0.0" />
</ItemGroup>
<ItemGroup>
<!-- Test dependencies -->
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="coverlet.collector" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" = />
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<!-- No Version attribute — managed centrally -->
<PackageReference Include="Microsoft.Extensions.Logging" />
</ItemGroup>
</Project>
When a specific project needs a different version (rare), use VersionOverride:
<PackageReference Include="Newtonsoft.Json" VersionOverride="13.0.3" />
Flag version overrides during code review — they defeat the purpose of CPM.
Place at the repo root to enforce consistent code style across all editors and the build.
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{csproj,props,targets,xml,json,yml,yaml}]
indent_size = 2
[*.cs]
# Namespace declarations
csharp_style_namespace_declarations = file_scoped:warning
# Braces
csharp_prefer_braces = true:warning
# var preferences
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Access modifiers
dotnet_style_require_accessibility_modifiers = always:warning
# Pattern matching
csharp_style_prefer_pattern_matching = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
# Null checking
csharp_style_prefer_null_check_over_type_check = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
# Expression-level preferences
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_properties = true:suggestion
= outside_namespace:warning
=
= private_fields
= camel_case_underscore
= warning
= field
= private
= _
= camel_case
See [skill:dotnet-add-analyzers] for full analyzer rule configuration.
Pin the SDK version for reproducible builds:
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestPatch"
}
}
Roll-forward policies:
latestPatch — allow patch updates only (recommended for CI)latestFeature — allow feature-band updates within the major versionlatestMajor — use whatever is installed (development convenience, not for CI)disable — exact version onlyConfigure package sources and security:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
The <clear /> + explicit sources + <packageSourceMapping> pattern prevents supply-chain attacks by ensuring packages only come from expected sources.
For private feeds, map internal package prefixes exclusively to the private source:
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="internal" value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="internal">
<package pattern="MyCompany.*" />
</packageSource>
</packageSourceMapping>
NuGet uses most-specific-pattern-wins precedence: MyCompany.Foo matches MyCompany.* (internal) over * (nuget.org), so internal packages restore exclusively from the private feed. This prevents dependency confusion attacks — an attacker cannot squat MyCompany.Foo on nuget.org because NuGet will never look there for packages matching MyCompany.*.
Do not map the same prefix to multiple sources unless you trust both — that defeats the protection.
.NET 9+ enables NuGetAudit by default, which checks for known vulnerabilities during restore. Configure the severity threshold:
<!-- In Directory.Build.props -->
<PropertyGroup>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>low</NuGetAuditLevel>
<NuGetAuditMode>all</NuGetAuditMode> <!-- audit direct + transitive -->
</PropertyGroup>
Enable deterministic restores with lock files:
<!-- In Directory.Build.props -->
<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
This generates packages.lock.json per project. Commit these files. In CI, restore with --locked-mode:
dotnet restore --locked-mode
For libraries published to NuGet:
<!-- In Directory.Build.props -->
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<DebugType>embedded</DebugType>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="all" />
</ItemGroup>
Key properties:
PublishRepositoryUrl — includes the repo URL in the NuGet packageEmbedUntrackedSources — embeds generated source filesDebugType=embedded — PDB embedded in the assembly (no separate symbol package needed)ContinuousIntegrationBuild — enables deterministic paths (only in CI to avoid breaking local debugging)