MS_NuGetDependencyBloat - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

NuGetでインストールすると依存関係が増えすぎる問題

概要

  • 何か NuGet パッケージをインストールすると、異常な程多くの、
    依存関係が package.config と *.csproj or *.vbproj ファイルに追加されるケースがある。

  • Visual Studio 2017 から Package Reference が利用可能になったが、
    一部問題もあり、package.config を使用し続けているケースもある。

補足(なぜ「異常な程多く」なるのか): 原因は
packages.config が推移的依存を「平坦に、すべて」列挙するためである。

【packages.config】
   自分が入れたもの      … Foo 1.0
   Foo が必要とするもの  … A 1.0, B 2.0
   A が必要とするもの    … C 1.0, D 3.0
   …すべてが packages.config と .csproj に並ぶ

【PackageReference】
   <PackageReference Include="Foo" Version="1.0" />
   …これ 1 行だけ。残りは復元時に自動解決される

つまり、原文の問題は
**「依存が多い」のではなく「多い依存が全部見えてしまう」**ことにある。

packages.config PackageReference
推移的依存の記載 すべて列挙される 書かれない(暗黙に解決)
参照設定(.csproj 1 件ずつ <Reference> が並ぶ <PackageReference> のみ
版の衝突 bindingRedirect で吸収 復元時に統一(最も新しい版)
不要な依存の削除 手で消しても復活する そもそも書かれない

根本的な解決は PackageReference への移行である
NuGet を使用したパッケージ管理
「Package Reference」節を参照)。

詳細

依存関係設定をミスっている

NuGet パッケージを作成する場合。

  • コチラに書いたように、
    nuspec ファイルへの依存関係設定をミスっているケースも多い。

  • このため、nuspec ファイルの依存関係設定を適切に行う必要がある。

補足(典型的なミス): パッケージを作る側の設定ミスとしては、
次の 2 つが多い。

dependenciesgroup で切っていない

<!-- 悪い:どのターゲットでもすべての依存が付く -->
<dependencies>
  <dependency id="System.Text.Json" version="8.0.0" />
  <dependency id="Newtonsoft.Json" version="13.0.3" />
</dependencies>

<!-- 良い:ターゲットごとに必要なものだけ -->
<dependencies>
  <group targetFramework="net48">
    <dependency id="Newtonsoft.Json" version="13.0.3" />
  </group>
  <group targetFramework="netstandard2.0">
    <dependency id="System.Text.Json" version="8.0.0" />
  </group>
</dependencies>

group を切らないと、
.NET Framework 側にも .NET Core 用の依存が流れ込む
これが原文の言う「異常な程多く」の一因になる
NuGetパッケージの開発と公開
targetFramework の節を参照)。

② 開発時にだけ必要なものを依存に含めている

<!-- 利用側に伝播させない(分析ツール、ソース ジェネレーター等) -->
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0">
  <PrivateAssets>all</PrivateAssets>
</PackageReference>

PrivateAssets="all" を付け忘れると、
自分のビルドにしか要らないものが、利用者にまで伝播する

dotnet pack で作る場合、.csprojPackageReference
そのまま nuspecdependencies になるため、
PrivateAssets の指定が実質的な nuspec 設定にあたる。

NuGet パッケージを集約するプロジェクトを作成

NuGet パッケージを利用する場合。

  • ウワモノのプロジェクトから直接、NuGet するのではなく、

    • NuGet パッケージを集約するプロジェクトを作成し、
    • そのプロジェクト出力を管理しておくと良いかも知れない。
  • ...と言うのも、

    • 自分が開発した「ライブラリ+テンプレート」のライブラリを NuGet 登録して、
    • リリース時に「NuGet+テンプレート」とテンプレートの NuGet 参照を変更するのだけど、
    • その際、不要な依存関係が大量に追加されるので、開発時のプロジェクト出力と比較して、
    • リリースの度に不要な依存関係をテンプレートの NuGet 参照から削除する手順が必要に
      なっているため。

補足(この回避策の評価): 「集約プロジェクトを 1 つ挟む」という
案は、packages.config 方式における現実的な緩和策である。

【直接参照】
   テンプレート.csproj ──> Foo(+推移的依存が全部書かれる)

【集約プロジェクトを挟む】
   テンプレート.csproj ──> 共通参照.csproj ──> Foo(依存はここに閉じる)

ただし、これは症状への対処であり、
PackageReference に移行すれば構造的に不要になる
現在の同種の要求には、次の標準機能で応えるのが定石である。

やりたいこと 現在の手段
版を 1 箇所で管理したい 中央パッケージ管理Directory.Packages.props
全プロジェクト共通の参照を入れたい Directory.Build.props
依存の集合をまとめて配りたい メタパッケージ(中身が無く依存だけを持つパッケージ)
<!-- Directory.Packages.props:版はここだけで決める -->
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
  </ItemGroup>
</Project>
<!-- Directory.Build.props:全プロジェクトに共通で入れる -->
<Project>
  <ItemGroup>
    <PackageReference Include="MyCompany.Common" />
  </ItemGroup>
</Project>

原文の「リリースの度に不要な依存関係を削除する手順」は、
移行すれば手順ごと消える種類の作業である。

補足(それでも依存が多いと感じる場合): PackageReference に
移行しても、実際にダウンロードされる依存の数が減るわけではない。
依存そのものを減らしたい場合は、次を確認する。

# 依存ツリーを可視化する(推移的依存も含めて表示)
dotnet list package --include-transitive

# 使われていない参照を検出する
dotnet tool install -g dotnet-outdated-tool
よくある原因 対処
メタパッケージを丸ごと参照 必要な個別パッケージだけにする
古い System.* 個別パッケージ netstandard2.0 以降では不要なものが多い
分析ツール/ジェネレーター PrivateAssets="all" を付ける
実質使っていない参照 削除する

なお、依存の数そのものは、必ずしも問題ではない
.NET Coreのデプロイ
トリミングを使えば、実際に使われない部分は出力から除去される。

参考

Microsoft Learn


Tags: 移行, .NET開発, デプロイ, NuGet

⚠️ **GitHub.com Fallback** ⚠️