依存関係

.NET ライブラリに依存関係を追加する主な方法は、NuGet パッケージを参照することです。 NuGet パッケージ参照を使用すると、既に記述した機能をすぐに再利用して活用できますが、これは .NET 開発者にとってよくある衝突の原因となります。 依存関係を正しく管理することは、他の .NET ライブラリの変更によって自分の .NET ライブラリと互換性がなくなる問題、またはその逆の問題を防ぐために重要です。

ひし形の依存関係

.NET プロジェクトの依存関係ツリーに複数のバージョンのパッケージがあることは一般的です。 たとえば、アプリが 2 つの NuGet パッケージに依存し、各パッケージが同じパッケージの異なるバージョンに依存しています。 これで、ひし形の依存関係がアプリの依存関係グラフに存在することになります。

Diamond dependency

ビルド時に、NuGet では、依存関係の依存関係を含め、プロジェクトが依存するすべてのパッケージが分析されます。 複数バージョンのパッケージが検出されると、規則が評価され、1 つが選択されます。 同じアプリケーション内で複数バージョンのアセンブリを並列して実行すると .NET で問題が発生するため、パッケージを統一する必要があります。

ほとんどのひし形の依存関係は簡単に解決できますが、状況によっては問題が発生することがあります。

  • 競合する NuGet パッケージ参照があると、パッケージの復元中にバージョンが解決されません。
  • バージョン間で破壊的変更があると、実行時にバグと例外が発生します。
  • パッケージ アセンブリが厳密な名前で、アセンブリ バージョンが変わり、アプリが .NET Framework 上で実行されている場合。 アセンブリのバインド リダイレクトが必要です。

自分のパッケージと共に使用されるパッケージを知ることはできません。 ライブラリが中断するひし形の依存関係の可能性を減らすには、依存しているパッケージの数を最小限に抑えることをお勧めします。

✔️ .NET ライブラリに不要な依存関係がないか確認してください。

NuGet の依存関係バージョンの範囲

パッケージ参照では、適用できる有効なパッケージの範囲を指定します。 通常、プロジェクト ファイルのパッケージ参照バージョンは最低バージョンであり、最高バージョンはありません。

<!-- Accepts any version 1.0 and above. -->
<PackageReference Include="ExamplePackage" Version="1.0" />

依存関係を解決するときに NuGet で使用される規則は複合ですが、NuGet の既定では適用可能な最低バージョンが検索されます。 互換性の問題が最も少ないのは最低バージョンなので、NuGet では、適用できる最高バージョンよりも適用できる最低バージョンが優先されます。

NuGet の適用できる最低バージョン規則があるので、最新のバージョンを取得しないようにパッケージ参照に上限のバージョンまたは正確な範囲を指定する必要はありません。 NuGet は、最低の最も互換性の高いバージョンを自動的に検索しようとします。

<!-- Accepts 1.0 up to 1.x, but not 2.0 and higher. -->
<PackageReference Include="ExamplePackage" Version="[1.0,2.0)" />

<!-- Accepts exactly 1.0. -->
<PackageReference Include="ExamplePackage" Version="[1.0]" />

上限のバージョンを指定すると、競合がある場合に NuGet が失敗します。 たとえば、あるライブラリが 1.0 のみを受け入れ、別のライブラリが 2.0 以降を必要としているとします。 バージョン 2.0 に互換性に影響する変更が組み込まれた可能性はありますが、厳密なバージョンやバージョンの上限が指定されていると、確実にエラーが発生します。

Diamond dependency conflict

❌ 最小バージョンを指定していない NuGet パッケージ参照を使用しないでください。

❌ 正確なバージョンを要求する NuGet パッケージ参照は回避してください。

❌ バージョンの上限がある NuGet パッケージ参照を使用しないでください。

詳細については、「Package versioning」(パッケージのバージョン管理) を参照してください。

NuGet 共有ソース パッケージ

外部 NuGet パッケージの依存関係を減らす方法の 1 つは、共有ソース パッケージを参照することです。 共有ソース パッケージには、参照時にプロジェクトに含まれるソース コード ファイルが含まれています。 プロジェクトの他の部分と共にコンパイルされたソース コード ファイルのみを含めているので、外部の依存関係がなく、競合が発生する可能性がありません。

共有ソース パッケージは、小規模な機能を組み込む場合に最適です。 たとえば、HTTP 呼び出しを行うためのヘルパー メソッドの共有ソース パッケージを参照できます。

Shared source package

<PackageReference Include="Microsoft.Extensions.Buffers.Testing.Sources" PrivateAssets="All" Version="1.0" />

Shared source project

共有ソース パッケージにはいくつの制限があります。 参照できるのは PackageReference からのみなので、以前の packages.config プロジェクトは除外されます。 また、共有ソース パッケージは、言語が同じプロジェクトからのみ使用できます。 これらの制限があるため、共有ソース パッケージは、オープン ソース プロジェクト内で機能を共有する場合に最適です。

✔️ 小規模な内部の機能の場合には、共有ソース パッケージを参照することを検討してください。

✔️ パッケージで小規模な内部の機能を提供する場合には、それを共有ソース パッケージにすることを検討してください。

✔️ 共有ソース パッケージは PrivateAssets="All" を使用して参照してください。

この設定で、これが開発時にのみ使用されるパッケージであり、パブリックな依存関係として公開されないように NuGet に指示します。

❌ パブリック API に共有ソース パッケージの種類を含めないでください。

共有ソースの種類は参照アセンブリにコンパイルされ、アセンブリ境界を越えて交換することはできません。 たとえば、あるプロジェクトの共有ソースの種類 IRepository は別のプロジェクトの同じ共有ソース IRepository とは別の種類です。 共有ソース パッケージの種類には internal の可視性が必要です。

❌ 共有ソース パッケージを NuGet.org に公開しないでください。

共有ソース パッケージにはソース コードが含まれており、言語の種類が同じプロジェクトでのみ使用できます。 たとえば、C# 共有ソース パッケージを F# アプリケーションで使用することはできません。

共有ソース パッケージは、プロジェクト内で内部的に使用するように、ローカル フィードまたは MyGet に公開します。