NuGet でのパッケージ依存関係の解決方法

パッケージがインストールまたは再インストールされるときは常に (復元プロセスの一部としてインストールされる場合も含めて)、NuGet はその最初のパッケージが依存する他のパッケージもすべてインストールします。

これらの直接依存するものにもそれぞれに独自の依存関係が存在する可能性があり、任意の深さまでそれが続きます。 これにより、依存関係グラフと呼ばれるものが生成されます。これには、すべてのレベルにおけるパッケージ間の関係が記述されています。

複数のパッケージに同じ依存関係がある場合、グラフに同じパッケージ ID が異なるバージョン制約で複数回出現する可能性があります。 ただし、プロジェクトで使用可能な指定されたパッケージのバージョンは 1 つのみであるため、NuGet は、使用されるバージョンを選択する必要があります。 実際のプロセスは、使われているパッケージ管理の形式によって異なります。

PackageReference による依存関係の解決

PackageReference 形式を使用してパッケージをプロジェクトにインストールする場合、NuGet は、フラットなパッケージ グラフへの参照を適切なファイルに追加して、競合を未然に解決します。 このプロセスは、"推移的な復元" と呼ばれます。 この場合、パッケージの再インストールまたは復元はグラフに列記されているパッケージをダウンロードするプロセスであり、結果としてビルドはいっそう高速で予測可能になります。

また、2.8.* などの浮動小数点バージョンを利用して、パッケージの最新バージョンを使用するようにプロジェクトが変更されないようにすることもできます。 フローティング バージョンを使用する場合は、ロック ファイル機能を有効にして、再現性を確保することをお勧めします。

ビルド以前に NuGet の復元プロセスを実行すると、最初にメモリ内で依存関係が解決された後、project.assets.json という名前のファイルに結果のグラフが書き込まれます。

この資産ファイルは MSBuildProjectExtensionsPath (既定でプロジェクトの 'obj' フォルダー) にあります。 その後、MSBuild はこのファイルを読み取り、潜在的な参照が見つかるフォルダーのセットに変換して、メモリ内のプロジェクト ツリーに追加します。

project.assets.json ファイルは一時的なものであるため、ソース管理に追加しないでください。 ロック ファイルは既定で .gitignore.tfignore の両方に一覧表示されます。 「パッケージとソース管理」をご覧ください。

依存関係の解決ルール

推移的な復元にあたり依存関係を解決するため、主に4 つのルールが適用されます。それは、適用可能な最低バージョンフローティングバージョン直接依存の優先関係間接依存関係です。

適用可能な最低バージョン

適用可能な最低バージョン ルールは、依存関係によって定義されている最低の可能なバージョンのパッケージを復元します。 また、浮動と宣言されていない限り、アプリケーションまたはクラス ライブラリの依存関係にも適用されます。

次の図では、たとえば 1.0-beta は 1.0 より低いと見なされるので、NuGet は 1.0 バージョンを選びます。

適用可能な最低バージョンの選択

次の図では、バージョン 2.1 はフィードで利用できませんが、バージョンの制約が 2.1 以上なので、NuGet は見つかった次に低いバージョンである 2.2 を選びます。

フィードで利用可能な次に低いバージョンの選択

アプリケーションで厳密なバージョン (1.2 など) が指定されていて、フィードでそれを利用できない場合、パッケージをインストールまたは復元しようとした NuGet はエラーで失敗します。

厳密に指定されたバージョンのパッケージを利用できない場合、NuGet はエラーを生成する

浮動小数点バージョン

浮動小数点の依存関係バージョンは * 文字で指定されます。 たとえば、6.0.* のようにします。 このバージョン指定では、"最新バージョン 6.0. x を使用する" となっており、4.* は、"最新の 4.x バージョンを使用する" ことを意味します。浮動小数点バージョンを使用すると、最新バージョンの依存関係で最新の状態を維持しながら、プロジェクト ファイルへの変更を減らすことができます。 フローティングバージョンは、プロジェクト レベルでのみ指定できます。

浮動小数点バージョンを使用すると、NuGet はバージョン パターンと一致する最高バージョンのパッケージを解決します。たとえば、6.0.* は 6.0 で始まるパッケージの最高バージョンになります。

浮動バージョン 6.0.* が要求されたときは、バージョン 6.0.1 を選ぶ

バージョン サーバーに存在するバージョン 解決方法 理由 メモ
* 1.1.0
1.1.1
1.2.0
1.3.0-alpha
1.2.0 最も高い安定バージョン。
1.1.* 1.1.0
1.1.1
1.1.2-alpha
1.2.0-alpha
1.1.1 指定したパターンに従う最も高い安定バージョン。
*-* 1.1.0
1.1.1
1.1.2-alpha
1.3.0-beta
1.3.0-beta 安定していないバージョンも含めた、最も高いバージョン。 Visual Studio バージョン 16.6、NuGet バージョン 5.6、.NET Core SDK バージョン 3.1.300 で使用可能。
1.1.*-* 1.1.0
1.1.1
1.1.2-alpha
1.1.2-beta
1.3.0-beta
1.1.2-beta 安定していないバージョンも含めて、パターンに従う最も高いバージョン。 Visual Studio バージョン 16.6、NuGet バージョン 5.6、.NET Core SDK バージョン 3.1.300 で使用可能。

Note

フローティング バージョン解決では、パッケージが一覧に含まれるかどうかは考慮されません。 フローティング バージョン解決は、グローバル パッケージ フォルダー内のパッケージで条件を満たすことができる場合、ローカルで解決されます。

直接的な依存関係の優先

アプリのパッケージ グラフに、複数のパッケージのバージョンが同じ部分グラフ内に含まれていて、かつそれらのバージョンの 1 つが部分グラフ内で直接の依存関係である場合、そのバージョンがその部分グラフに対して選択され、残りの関係は無視されます。 この動作により、アプリケーションは依存関係グラフ内で特定のパッケージのバージョンをオーバーライドできます。

次の例では、アプリケーションはバージョン制約が 2.0.0 以上でパッケージ B に直接依存しています。 また、アプリケーションはパッケージ A にも依存していますが、パッケージ A も 1.0 0 以上の制約でパッケージ B に依存しています。 パッケージ B 2.0.0 に対する依存関係が、グラフのアプリケーションと直接依存の関係にあるので、バージョン 2.0 が使われます。

直接依存関係優先ルールを使用するアプリケーション

警告

直接依存の優先が適用された結果、パッケージのバージョンのダウングレードが発生することがあるので、グラフ内の他の依存関係が損なわれる可能性があります。 パッケージがダウングレードされると、NuGet がユーザーに対して警告を行います。

また、このルールにより、大きな依存関係グラフが効率を上げることにつながります。 同じ部分グラフのより近い依存関係がより高いバージョンにある場合、NuGet はその依存関係を無視し、NuGet はグラフ内で分岐している残りの依存関係もすべて無視します。

たとえば、次の図では、パッケージ C 2.0.0 が使われるため、NuGet は古いバージョンのパッケージ C を参照している部分グラフ内のすべての分岐を無視します。

NuGet は、グラフ内のパッケージを無視するとき、その分岐全体を無視する

この規則を通じて、NuGet はパッケージ作成者の意図を汲み取ることができるのです。 次の図では、パッケージ A の作成者がパッケージ C 2.0.0 からパッケージ C 1.0.0 にダウングレードしています。

パッケージ作成者が明示的にダウングレードすると、NuGet はそれを優先します。

アプリの所有者は、パッケージ C を 2.0.0 より高いバージョンにアップグレードすることを選択できるため、パッケージ C のバージョンをそれ以上ダウングレードすることはできません。この場合、警告は表示されません。

ダウングレードされたパッケージの直接の依存関係をアプリケーションが追加すると、NuGet はそれを優先します。

いとこ依存関係

アプリのグラフ内で異なる部分グラフで、異なるパッケージのバージョンが参照されている場合、NuGet はすべてのバージョン要件が満たされる最低バージョンを使用します (適用可能な最低バージョンフローティングバージョン のルールと同様)。 たとえば、次の図では、パッケージ B のバージョン 2.0.0 は、他の 1.0.0 以上という制約を満たすので、使用されます。

すべての制約を満たす最低バージョンを使う、いとこ依存関係の解決

間接依存関係のルールを適用するために、パッケージを同じ距離に置く必要はありません。 次の図では、パッケージ D 2.0.0 が Package C の部分グラフで選択され、パッケージ D 3.0.0 がパッケージ A の部分グラフで選択されています。アプリの部分グラフには、パッケージ D に直接依存の関係はないため、適用可能な最低バージョン のルールが適用され、バージョン 3.0.0 が選択されます。

異なる距離ですべての制約を満たす下位バージョンを使用した、いとこ依存関係の解決

状況によっては、すべてのバージョン要件を満たすことができない場合があります。 次に示すように、パッケージ A ではパッケージ B 1.0.0 だけが必要であり、パッケージ C ではパッケージ B 2.0.0 以上が必要である場合、NuGet は依存関係を解決できずエラーを生成します。

厳密なバージョン要件のために解決できない依存関係

このような状況では、最上位のコンシューマー (アプリケーションまたはパッケージ) がパッケージ B に対する独自の直接依存関係を追加して、直接依存の優先のルールが適用されるようにする必要があります。

PackageReference を使用したバージョン範囲とプレリリース版

パッケージに安定版とプレリリース版の両方があることは珍しくありません。 依存関係グラフを解決する場合、NuGet は、次の 1 つのルールに基づいてパッケージのプレリリース バージョンを考慮するかどうかを判断します。: If the project or any packages within the graph request a prerelease version of a package, then include both prerelease or stable versions, otherwise consider stable versions only.

実際には、これは、適用可能な最も低いルールの下で次のことを意味します。

バージョン範囲 利用できるバージョン 選択したバージョン
[1.0.0, 2.0.0) 1.2.0-beta.1, 1.2.0, 1.2.0
[1.0.0, 2.0.0-0) 1.2.0-beta.1, 1.2.0, 1.2.0-beta.1
[1.0.0, 2.0.0) 1.2.0-beta.1, 2.0.0-beta.3 なし、NU1103 が発生します。
[1.0.0, 2.0.0-rc) 1.2.0-beta.1, 2.0.0-beta.3 1.2.0-beta.1

packages.config での依存関係の解決

packages.config では、プロジェクトの依存関係はフラット リストとして packages.config に書き込まれます。 これらのパッケージの依存関係も、同じリストに書き込まれます。 パッケージがインストールされると、NuGet は .csproj ファイル、app.configweb.config、および他の個別ファイルも変更する場合があります。

packages.config の場合、NuGet は個々のパッケージのインストール中に依存関係の競合の解決を試みます。 つまり、パッケージ B に依存するパッケージ A がインストールされていて、パッケージ B が別のものの依存関係として packages.config のリストに既に含まれる場合、NuGet は要求されているパッケージ B のバージョンを比較し、すべてのバージョン制約を満たすバージョンの発見を試みます。 具体的には、NuGet は依存関係を満たす低い方の major.minor バージョンを選びます。

NuGet 2.8 は、既定により、最も低い "パッチ" バージョンを探します (「NuGet 2.8 のリリース ノート」を参照してください)。 この設定は、NuGet.ConfigDependencyVersion 属性およびコマンド ラインの -DependencyVersion スイッチで制御できます。

packages.config の依存関係解決プロセスは、大規模な依存関係グラフでは複雑になります。 パッケージの各新規インストールでは、グラフ全体を走査して、バージョン間の競合の可能性を明らかにする必要があります。 競合が発生するときは、インストールが停止されて、プロジェクトは不明な状態のままになります (特に、プロジェクト ファイル自体への変更の可能性がある場合)。 他のパッケージ管理形式を使うと、このような問題はありません。

バージョン範囲とプレリリース バージョン (packages.config を使用)

packages.config の解決では、グラフ内で、安定版とプレリリースからの依存関係を混在させることはできません。 依存関係が [1.0.0, 2.0.0) のような範囲で表されている場合、プレリリース パッケージはグラフで許可されません。

依存関係アセットの管理

PackageReference 形式を使用すると、依存関係から最上位のプロジェクトへの資産のフローを制御できます。 詳細については、PackageReference に関するページを参照してください。

最上位レベルのプロジェクト自体がパッケージである場合は、include および exclude 属性と .nuspec ファイルの依存関係リストを使って、このフローを制御することもできます。 .nuspec リファレンスの依存関係をご覧ください。

参照の除外

同じ名前のアセンブリがプロジェクト内の複数の箇所で参照されていて、設計時エラーおよびビルド時エラーが発生することがあります。 あるプロジェクトは、C.dll のカスタム バージョンを含み、やはり C.dll を含むパッケージ C を参照しているものとします。 同時に、このプロジェクトはパッケージ B にも依存しており、パッケージ B もパッケージ C と C.dll に依存しています。 結果として、NuGet はどの C.dll を使えばよいのか決定できず、かといってパッケージ B もパッケージ C に依存しているため、プロジェクトでのパッケージ C に対する依存関係を単に削除するわけにもいきません。

これを解決するには、必要な C.dll を直接参照し (または、適切なパッケージを参照している別のパッケージを使い)、すべての資産を除外するパッケージ C への依存関係を追加します。 これは、使われているパッケージ管理形式に応じて、次のように行われます。

  • PackageReference: 依存関係に ExcludeAssets="All" を追加します。

    <PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
    
  • packages.config: 必要なバージョンの C.dll だけを参照するように、.csproj ファイルからパッケージ C への参照を削除します。

パッケージをインストールする間の依存関係の更新

依存関係のバージョンが既に満たされている場合、他のパッケージのインストール中に依存関係は更新されません。 たとえば、パッケージ A はパッケージ B に依存し、バージョン番号として 1.0 が指定されているものとします。 ソース リポジトリには、パッケージ B のバージョン 1.0、1.1、および 1.2 が含まれます。B のバージョン 1.0 が既に含まれているプロジェクトに A をインストールした場合、B 1.0 はバージョン制約を満たしているので、そのまま使用されます。 ただし、パッケージ A で B のバージョン 1.1 以上が要求されていた場合は、B 1.2 がインストールします。

互換性のないパッケージのエラーの解決

パッケージの復元操作の間に、"1 つ以上のパッケージは <プロジェクトのターゲット フレームワーク> と互換性がありません" またはパッケージはプロジェクトのターゲット フレームワークと "互換性がありません" という内容のエラーが表示されることがあります。

このエラーは、プロジェクトで参照されているパッケージの 1 つ以上が、プロジェクトのターゲット フレームワークをサポートしていることを示さない場合に発生します。つまり、パッケージの lib フォルダーには、プロジェクトと互換性のあるターゲット フレームワーク用の適切な DLL が含まれません (「Target frameworks」(ターゲット フレームワーク) をご覧ください)。

たとえば、プロジェクトのターゲットが netstandard1.6 で、lib\net20 フォルダーと \lib\net45 フォルダーのみに DLL が含まれるパッケージをインストールしようとすると、パッケージおよび場合によっては依存関係について、次のようなメッセージが表示されます。

Restoring packages for myproject.csproj...
Package ContosoUtilities 2.1.2.3 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6). Package ContosoUtilities 2.1.2.3 supports:
  - net20 (.NETFramework,Version=v2.0)
  - net45 (.NETFramework,Version=v4.5)
Package ContosoCore 0.86.0 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6). Package ContosoCore 0.86.0 supports:
  - 11 (11,Version=v0.0)
  - net20 (.NETFramework,Version=v2.0)
  - sl3 (Silverlight,Version=v3.0)
  - sl4 (Silverlight,Version=v4.0)
One or more packages are incompatible with .NETStandard,Version=v1.6.
Package restore failed. Rolling back package changes for 'MyProject'.

互換性の問題を解決するには、次のいずれかのようにします。

  • 使うパッケージでサポートされているフレームワークに、プロジェクトのターゲットを変更します。
  • パッケージの作成者に連絡し、協力して、選んだフレームワークに対するサポートを追加します。 nuget.org の各パッケージ リスト ページには、そのための [Contact Owners] リンクがあります。

ヒント

代替ソリューション: NuGetSolver は、依存関係の競合の解決を支援することを目的とする、Microsoft DevLabs によって開発された Visual Studio 拡張機能です。 これにより、これらの issue を特定して対処するプロセスが自動化されます。 詳細については、Visual Studio Marketplace の NuGetSolver ページを参照してください。エクスペリエンスに関するフィードバックをお聞かせください。