C#(.NET)プロジェクトでNuGetパッケージを管理する方法はいくつかありますが、Visual Studio 2022 17.2以降 (NuGet 6.2以降)で導入されたCentral Package Managementはプロジェクトのパッケージ管理を簡単にする新しい方法です。これを使うと複数のcsprojファイルを持つソリューションで、パッケージとバージョンを一元管理できるため、今後の標準になっていくと予想されます。
.csprojのNuGetパッケージ定義からCentral Package Managementへの移行はこれまで手作業でしたが、Visual Studio 2022と.NET Upgrade Assistant拡張
を使うと簡単に移行できるようになったので紹介します。
- .NET Upgrade Assistantとは
- Central Package Managementに移行するとどうなる?
- .NET Upgrade AssistantでCentral Package Managementに移行する
- Dockerビルドとの相性
- まとめ
.NET Upgrade Assistantとは
.NET Upgrade Assistant | Marketplace VisualStudioは、.NETアプリケーションを最新の.NETバージョンにアップグレードするためのツールです。.NET Frameworkから.NET 6+(.NET 9.0含む)へアップグレードや、.NET Coreから.NET 6+(.NET 9.0含む)へのアップグレードもできます。1
先日0.5.793.65096がリリースされCentral Package Managemen移行もサポートされました。うれしいですね。
.NET Upgrade Assistant Now Supports Upgrading to Centralized Package Mangement | .NET Blog
.NET Upgrade Assistantをインストールする
.NET Upgrade AssistantはVisual Studio 2022 17.1以降で利用できます。Visual Studioのメニューバー > Manage Extensions > .NET Upgrade Assistantを検索してインストールします。
細かい手順は以下のリンクを参照してください。
Central Package Managementに移行するとどうなる?
サンプルに次のようなプロジェクトを用意します。
CPMSample │ CPMSample.sln │ ├─ConsoleApp │ ConsoleApp.csproj │ Program.cs │ └─TestProject1 TestProject1.csproj UnitTest1.cs
Central Package Management移行前
NuGetパッケージはそれぞれのcsprojファイルで参照されています。
- ConsoleApp.csproj
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net9.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="ConsoleAppFramework" Version="5.3.3"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="ProcessX" Version="1.5.5" /> </ItemGroup> </Project>
- TestProject1.csproj
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net9.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> <PackageReference Include="coverlet.collector" Version="6.0.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> <PackageReference Include="xunit" Version="2.9.2" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" /> </ItemGroup> <ItemGroup> <Using Include="Xunit" /> </ItemGroup> </Project>
Central Package Management移行後
このプロジェクトをCentral Package Managementに移行すると次のような構成になります。Directory.Packages.propsファイルが追加されています。Central Package Managementはプロジェクトのルートディレクトリに配置されたDirectory.Packages.props
ファイルでパッケージとバージョンを一元管理します。2 移行後にcsprojファイルをみると、csprojで指定していたパッケージのバージョン指定がなくなっています。
CPMSample │ CPMSample.sln │ Directory.Packages.props │ ├─ConsoleApp │ ConsoleApp.csproj │ Program.cs │ └─TestProject1 TestProject1.csproj UnitTest1.cs
- Directory.Packages.props
<Project> <PropertyGroup> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled> <!-- TransitivePinningを無効にするとfalse --> <!-- <CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled> --> <NoWarn>$(NoWarn);NU1507</NoWarn> </PropertyGroup> <ItemGroup> <PackageVersion Include="ConsoleAppFramework" Version="5.3.3" /> <PackageVersion Include="coverlet.collector" Version="6.0.2" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> <PackageVersion Include="ProcessX" Version="1.5.5" /> <PackageVersion Include="xunit" Version="2.9.2" /> <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" /> </ItemGroup> </Project>
- ConsoleApp.csproj
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net9.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="ConsoleAppFramework"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="ProcessX" /> </ItemGroup> </Project>
- TestProject1.csproj
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net9.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> <PackageReference Include="coverlet.collector" /> <PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="xunit" /> <PackageReference Include="xunit.runner.visualstudio" /> </ItemGroup> <ItemGroup> <Using Include="Xunit" /> </ItemGroup> </Project>
Central Package Managementのメリットと注意点
すべてのcsprojで使っているNuGetパッケージとバージョンがDirectory.Packages.props
に記載されているので、パッケージを簡単に確認できるようになりました。
Directory.Packages.props
ファイルのManagePackageVersionsCentrally
とPackageVersion
に注目してください。ManagePackageVersionsCentrally
はCentral Package Managementを有効にするためのプロパティです。.csprojに直接定義するとそのcsprojだけをCentral Package Managementに移行できますが、アップグレードアシスタントを使うと、Directory.Packages.props
に記載されて複数のcsprojを一括でCentral Package Managementに移行できます。PackageVersion
はManagePackageVersionsCentrallyで指定したパッケージのバージョンを一元管理するためのプロパティです。3
Central Package Management移行後、csprojでパッケージのバージョンをVerison
で指定するとエラーになります。csprojでバージョンを指定するには<PackageReference Include="PackageA" VersionOverride="3.0.0" />
のようにVersionOverride
を使います。csprojに例外を作るとパッケージバージョン管理が難しくなるので個別指定はおすすめしませんが、例外的にバージョンを固定したい場合に使えます。
.NET Upgrade AssistantでCentral Package Managementに移行する
.NET Upgrade AssistantでCentral Package Managementに移行する手順は次の通りです。
1. Visual Studioでソリューションファイルを開く
Visual Studioでソリューションファイルを開きます。.csprojじゃないので注意してください。
2. 適当なcsprojを右クリック > Upgrade
を選択
3. メニューからNuGet central package management (CPM)を選択する
Upgrade AssistantのメニューにNuGet upgradesがあります。これを選択します。
※ 注意: ソリューションを右クリック >
Upgrade
を選択しても、Central Package Managementに移行メニューが出ないので注意してください。↓はSolution > Upgradeを選択したときの画面ですが、NuGet upgradesが存在しません。
4. Central Package Managementに移行するプロジェクトを選択する
Central Package Managementに移行するプロジェクトを選択します。可能なら全プロジェクトを選択すると一元管理できていいですね。
5. プロジェクトのカスタマイズ
Central Package Managementに移行するプロジェクトを選択すると、プロジェクトのカスタマイズ画面が表示されます。ここでDirectory.Packages.props
の場所を指定します。デフォルトではソリューションファイルと同じディレクトリに配置されます。
また、Enable transitive pinning
でTransitive Pinningを有効にするか選べます。Transitive Pinningは、推移的なパッケージ(依存しているパッケージが依存しているパッケージ)のバージョンを固定する機能です。Transitive Pinningを有効にすると、推移的な依存関係が暗黙的にトップレベルの依存関係に固定されます。4.NETチームはTransitive Pinning有効を推奨にしています。
Transitive Pinningを有効にすると、NuGetパッケージが参照しているライブラリも更新されるため動作担保できない可能性があります。が、推移的パッケージのバージョンもコントロールされているほうがセキュリティ管理しやすいので、有効にできるといいですね。
6. 移行実行
正常に移行できると、次のようにチェックマークがつきます。また、Directory.Packages.props
ファイルが作成されます。
Dockerビルドとの相性
Dockerビルド時にdotnet build
している場合、Directory.Packages.propsがないとエラーになります。場合によっては、Dockerビルド開始前にDirectory.Packages.propsをコピーする処理を追加する必要があります。
C#の.csprojごとにDockerビルドすると、Dockerfileにプロジェクトが参照しているcsprojを引っ張る処理が書かれます。ただDirectory.Packages.propsは追加されず、またDockerコンテキストパスよりも上位に配置されていることもあり相性が悪いです。
C#プロジェクト全般に言えるのですが、Dockerビルドするならsln以下のディレクトリ構成ごとDockerコンテキストに持ってきて、まとめてビルドするとシンプルで管理しやすくなります。あるいはホストマシンでdllビルドしてDockerコンテナにコピーする方法もあります。
× これは面倒 1. csprojごとにDockerコンテキストへコピー 2. Directory.Packages.propsをコピー 3. dotnet restore & dotnet build 〇 これが簡単でおすすめ 1. slnの構造ごとDockerコンテキストへコピー 2. dotnet restore & dotnet build
まとめ
これまで手作業でCentral Package Managementに移行していたので、.NET Upgrade Assistantで簡単に移行できるのはとても便利です。これでDirectory.Package.props
を作って、csprojを1つ1つ確認していく手間が省けます。
運用を考えるとOSSライブラリの管理は重要です。Central Package Managementを使ってパッケージのバージョンを一元管理することで、ライセンス情報を一元管理できるので、セキュリティやライセンス管理にも役立ちます。ぜひ快適なNuGetパッケージ管理を試してみてください。