dotnet-project-structure について
.NET ソリューションを構築する開発者向けに、ソリューション構成、ビルド設定の集約、バージョン管理を解説するスキルです。.slnx、NuGet パッケージバージョンの一元管理、SDK の固定、リリースノートを使ったバージョン更新を扱います。
主な機能
- 01.slnx ソリューションの作成と移行
- 02ビルドプロパティの集約
- 03NuGet パッケージバージョンの一元管理
- 04global.json による SDK の固定
- 05リリースノートに基づくバージョン更新
活用シーン
- 01新しい .NET ソリューションのファイル構成を計画
- 02複数プロジェクトのビルド設定とパッケージバージョンを統一
- 03環境間で指定 SDK バージョンを使用
- 04リリースノートをバージョン更新とビルドに連携
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ aaronontheweb/ dotnet-skills --skill dotnet-project-structureインストール後、Claude Code で/dotnet-project-structureを入力して読み込まれたか確認します。
詳細
.NET プロジェクト構成とビルド構成
このスキルを使用するタイミング
次の場合にこのスキルを使用します:
- 最新のベストプラクティスに沿って新しい .NET ソリューションをセットアップするとき
- 複数のプロジェクトに適用するビルドプロパティを一元管理するとき
- パッケージバージョンの一元管理を実装するとき
- デバッグと NuGet パッケージのために SourceLink をセットアップするとき
- リリースノートを使ったバージョン管理を自動化するとき
- 一貫したビルドのために SDK バージョンを固定するとき
関連スキル
dotnet-local-tools- dotnet-tools.json を使用したローカル .NET ツールの管理microsoft-extensions-configuration- 構成の検証パターン
ソリューションファイル形式(.slnx)
.slnx 形式は、.NET 9 で導入された XML ベースの最新のソリューションファイル形式です。従来の .sln 形式に代わるものです。
従来の .sln と比較した利点
| 項目 | .sln(従来) | .slnx(最新) |
|---|---|---|
| 形式 | 独自のテキスト形式 | 標準的な XML |
| 読みやすさ | GUID と難解な構文 | 明瞭で人が読みやすい |
| バージョン管理 | 差分やマージが困難 | 差分やマージが容易 |
| 編集 | IDE が必要 | 任意のテキストエディター |
バージョン要件
| ツール | 最小バージョン |
|---|---|
| .NET SDK | 9.0.200 |
| Visual Studio | 17.13 |
| MSBuild | Visual Studio Build Tools 17.13 |
注: .NET 10 以降では、dotnet new sln はデフォルトで .slnx ファイルを作成します。.NET 9 では、明示的に移行するか、形式を指定する必要があります。
.slnx ファイルの例
<Solution>
<Folder Name="/build/">
<File Path="Directory.Build.props" />
<File Path="Directory.Packages.props" />
<File Path="global.json" />
<File Path="NuGet.Config" />
<File Path="README.md" />
</Folder>
<Folder Name="/src/">
<Project Path="src/MyApp/MyApp.csproj" />
<Project Path="src/MyApp.Core/MyApp.Core.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/MyApp.Tests/MyApp.Tests.csproj" />
</Folder>
</Solution>
.sln から .slnx への移行
dotnet sln migrate コマンドを使用して、既存のソリューションを変換します:
# Migrate a specific solution file
dotnet sln MySolution.sln migrate
# Or if only one .sln exists in the directory, just run:
dotnet sln migrate
重要: 同じリポジトリに .sln ファイルと .slnx ファイルの両方を置かないでください。ソリューションの自動検出で問題が発生し、同期に支障をきたすことがあります。移行後は、古い .sln ファイルを削除してください。
Visual Studio で移行することもできます:
- ソリューションを開きます
- ソリューション エクスプローラーでソリューションを選択します
- File > Save Solution As... に移動します
- 「Save as type」を Xml Solution File (*.slnx) に変更します
新しい .slnx ソリューションの作成
# .NET 10+: Creates .slnx by default
dotnet new sln --name MySolution
# .NET 9: Specify the format explicitly
dotnet new sln --name MySolution --format slnx
# Add projects (works the same for both formats)
dotnet sln add src/MyApp/MyApp.csproj
推奨事項
.NET 9.0.200 以降を使用している場合は、ソリューションを .slnx に移行してください。 多くの利点があります:
- マージの競合が大幅に減少します(ランダムな GUID が変わらないため)
- 人が読みやすく、任意のテキストエディターで編集できます
- 最新の
.csproj形式と一貫性があります - プルリクエストでの差分確認やレビューがしやすくなります
Directory.Build.props
Directory.Build.props は、ディレクトリツリー内のすべてのプロジェクトに適用されるビルド構成を一元管理します。ソリューションのルートに配置します。
完全な例
<Project>
<PropertyGroup>
<Authors>Your Team</Authors>
<Company>Your Company</Company>
<Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Your Company</Copyright>
<Product>Your Product</Product>
<PackageProjectUrl>https://github.com/yourorg/yourrepo</PackageProjectUrl>
<RepositoryUrl>https://github.com/yourorg/yourrepo</RepositoryUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageTags>your;tags;here</PackageTags>
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<PackageReleaseNotes>See RELEASE_NOTES.md</PackageReleaseNotes>
</PropertyGroup>
<PropertyGroup>
<NetStandardLibVersion>netstandard2.0</NetStandardLibVersion>
<NetLibVersion>net8.0</NetLibVersion>
<NetTestVersion>net9.0</NetTestVersion>
</PropertyGroup>
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)logo.png" Pack="true" PackagePath="\" />
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<PropertyGroup>
<PackageIcon>logo.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Collections.Immutable" />
</ItemGroup>
</Project>
主なパターン
動的な著作権年
<Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Your Company</Copyright>
MSBuild のプロパティ関数を使用して、ビルド時に現在の年を挿入します。手動で更新する必要はありません。
再利用可能なターゲット フレームワークのプロパティ
ターゲット フレームワークを一度定義し、各所で参照します:
<PropertyGroup>
<NetLibVersion>net8.0</NetLibVersion>
<NetTestVersion>net9.0</NetTestVersion>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>$(NetLibVersion)</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>$(NetTestVersion)</TargetFramework>
</PropertyGroup>
NuGet パッケージの SourceLink
SourceLink を使用すると、NuGet パッケージのステップ実行デバッグが可能になります:
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
</ItemGroup>
Directory.Packages.props - パッケージの一元管理
パッケージの一元管理(CPM)により、すべての NuGet パッケージバージョンの信頼できる唯一の情報源を用意できます。
セットアップ
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<PropertyGroup>
<AkkaVersion>1.5.35</AkkaVersion>
<AspireVersion>9.1.0</AspireVersion>
</PropertyGroup>
<ItemGroup Label="App Dependencies">
<PackageVersion Include="Akka" Version="$(AkkaVersion)" />
<PackageVersion Include="Akka.Cluster" Version="$(AkkaVersion)" />
<PackageVersion Include="Akka.Persistence" Version="$(AkkaVersion)" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
</ItemGroup>
<ItemGroup Label="Build Dependencies">
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
</ItemGroup>
<ItemGroup Label="Test Dependencies">
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1" />
<PackageVersion Include="FluentAssertions" Version="7.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.3" />
</ItemGroup>
</Project>
パッケージの使用(バージョン指定不要)
<ItemGroup>
<PackageReference Include="Akka" />
<PackageReference Include="Akka.Cluster" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="xunit" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>
利点
- 信頼できる唯一の情報源 - すべてのバージョンを1つのファイルで管理します
- バージョンの不整合を防止 - すべてのプロジェクトで同じバージョンを使用します
- 簡単な更新 - 一度変更するだけで、すべてに適用されます
- パッケージのグループ化 - 関連するパッケージ(例:すべての Akka パッケージ)にバージョン変数を使用します
global.json - SDK バージョンの固定
すべての環境で一貫したビルドを行うために、.NET SDK のバージョンを固定します。
{
"sdk": {
"version": "9.0.200",
"rollForward": "latestFeature"
}
}
ロールフォワードポリシー
| ポリシー | 動作 |
|---|---|
disable | 正確なバージョンが必要です |
patch | 同じ major.minor の最新 patch |
feature | 同じ major の最新 minor.patch |
latestFeature | 同じ major の最新 feature バンド |
minor | 同じ major の最新 minor |
latestMinor | 同じ major の最新 minor |
major | 最新の SDK(推奨されません) |
推奨: latestFeature - 同じ feature バンド内での patch 更新を許可します。
RELEASE_NOTES.md によるバージョン管理
リリースノートの形式
#### 1.2.0 January 15th 2025 ####
- Added new feature X
- Fixed bug in Y
- Improved performance of Z
#### 1.1.0 December 10th 2024 ####
- Initial release with features A, B, C
解析スクリプト(getReleaseNotes.ps1)
function Get-ReleaseNotes {
param (
[Parameter(Mandatory=$true)]
[string]$MarkdownFile
)
$content = Get-Content -Path $MarkdownFile -Raw
$sections = $content -split "####"
$result = [PSCustomObject]@{
Version = $null
Date = $null
ReleaseNotes = $null
}
if ($sections.Count -ge 3) {
$header = $sections[1].Trim()
$releaseNotes = $sections[2].Trim()
$headerParts = $header -split " ", 2
if ($headerParts.Count -eq 2) {
$result.Version = $headerParts[0]
$result.Date = $headerParts[1]
}
$result.ReleaseNotes = $releaseNotes
}
return $result
}
バージョン更新スクリプト(bumpVersion.ps1)
function UpdateVersionAndReleaseNotes {
param (
[Parameter(Mandatory=$true)]
[PSCustomObject]$ReleaseNotesResult,
[Parameter(Mandatory=$true)]
[string]$XmlFilePath
)
$xmlContent = New-Object XML
$xmlContent.Load($XmlFilePath)
# Update VersionPrefix
$versionElement = $xmlContent.SelectSingleNode("//VersionPrefix")
$versionElement.InnerText = $ReleaseNotesResult.Version
# Update PackageReleaseNotes
$notesElement = $xmlContent.SelectSingleNode("//PackageReleaseNotes")
$notesElement.InnerText = $ReleaseNotesResult.ReleaseNotes
$xmlContent.Save($XmlFilePath)
}
ビルドスクリプト(build.ps1)
# Load helper scripts
. "$PSScriptRoot\scripts\getReleaseNotes.ps1"
. "$PSScriptRoot\scripts\bumpVersion.ps1"
# Parse release notes and update Directory.Build.props
$releaseNotes = Get-ReleaseNotes -MarkdownFile (Join-Path -Path $PSScriptRoot -ChildPath "RELEASE_NOTES.md")
UpdateVersionAndReleaseNotes -ReleaseNotesResult $releaseNotes -XmlFilePath (Join-Path -Path $PSScriptRoot -ChildPath "Directory.Build.props")
Write-Output "Updated to version $($releaseNotes.Version)"
CI/CD の統合
# GitHub Actions example
- name: Update version from release notes
shell: pwsh
run: ./build.ps1
- name: Build
run: dotnet build -c Release
- name: Pack with tag version
run: dotnet pack -c Release /p:PackageVersion=${{ github.ref_name }}
- name: Push to NuGet
run: dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
NuGet.Config
NuGet のソースと動作を設定します:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
主な設定:
<clear />- 再現可能なビルドのため、継承されたソースおよび既定のソースを削除しますdisableSourceControlIntegration- TFS/Git の統合に関する問題を防ぎます
プロジェクト構造の全体像
MySolution/
├── .config/
│ └── dotnet-tools.json # Local .NET tools
├── .github/
│ └── workflows/
│ ├── pr-validation.yml # PR checks
│ └── release.yml # NuGet publishing
├── scripts/
│ ├── getReleaseNotes.ps1 # Parse RELEASE_NOTES.md
│ └── bumpVersion.ps1 # Update Directory.Build.props
├── src/
│ ├── MyApp/
│ │ └── MyApp.csproj
│ └── MyApp.Core/
│ └── MyApp.Core.csproj
├── tests/
│ └── MyApp.Tests/
│ └── MyApp.Tests.csproj
├── Directory.Build.props # Centralized build config
├── Directory.Packages.props # Central package versions
├── MySolution.slnx # Modern solution file
├── global.json # SDK version pinning
├── NuGet.Config # Package source config
├── build.ps1 # Build orchestration
├── RELEASE_NOTES.md # Version history
├── README.md # Project documentation
└── logo.png # Package icon
クイックリファレンス
| ファイル | 用途 |
|---|---|
MySolution.slnx | 最新の XML ソリューションファイル |
Directory.Build.props | ビルドプロパティの一元管理 |
Directory.Packages.props | パッケージバージョンの一元管理 |
global.json | SDK バージョンの固定 |
NuGet.Config | パッケージソースの設定 |
RELEASE_NOTES.md | バージョン履歴(ビルド時に解析) |
build.ps1 | ビルドオーケストレーションスクリプト |
.config/dotnet-tools.json | ローカル .NET ツール |