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 はエラーで失敗します。
浮動小数点バージョン
浮動小数点の依存関係バージョンは * 文字で指定されます。 たとえば、6.0.*
のようにします。 このバージョン指定では、"最新バージョン 6.0. x を使用する" となっており、4.*
は、"最新の 4.x バージョンを使用する" ことを意味します。浮動小数点バージョンを使用すると、最新バージョンの依存関係で最新の状態を維持しながら、プロジェクト ファイルへの変更を減らすことができます。
フローティングバージョンは、プロジェクト レベルでのみ指定できます。
浮動小数点バージョンを使用すると、NuGet はバージョン パターンと一致する最高バージョンのパッケージを解決します。たとえば、6.0.*
は 6.0 で始まるパッケージの最高バージョンになります。
バージョン | サーバーに存在するバージョン | 解決方法 | 理由 | メモ |
---|---|---|---|---|
* | 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 はパッケージ作成者の意図を汲み取ることができるのです。 次の図では、パッケージ A の作成者がパッケージ C 2.0.0 からパッケージ C 1.0.0 にダウングレードしています。
アプリの所有者は、パッケージ C を 2.0.0 より高いバージョンにアップグレードすることを選択できるため、パッケージ C のバージョンをそれ以上ダウングレードすることはできません。この場合、警告は表示されません。
いとこ依存関係
アプリのグラフ内で異なる部分グラフで、異なるパッケージのバージョンが参照されている場合、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.config
、web.config
、および他の個別ファイルも変更する場合があります。
packages.config
の場合、NuGet は個々のパッケージのインストール中に依存関係の競合の解決を試みます。 つまり、パッケージ B に依存するパッケージ A がインストールされていて、パッケージ B が別のものの依存関係として packages.config
のリストに既に含まれる場合、NuGet は要求されているパッケージ B のバージョンを比較し、すべてのバージョン制約を満たすバージョンの発見を試みます。 具体的には、NuGet は依存関係を満たす低い方の major.minor バージョンを選びます。
NuGet 2.8 は、既定により、最も低い "パッチ" バージョンを探します (「NuGet 2.8 のリリース ノート」を参照してください)。 この設定は、NuGet.Config
の DependencyVersion
属性およびコマンド ラインの -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 ページを参照してください。エクスペリエンスに関するフィードバックをお聞かせください。