プロジェクトによって参照されるアセンブリを選択する

アセンブリは、ビルド時に 2 つの異なる方法で使われます。 1 つ目はコンパイル用です。パッケージ コンシューマーのコードをアセンブリ内の API に対してコンパイルし、Intellisense で提案を表示できるようにします。 2 つ目はランタイムです。アセンブリは bin ディレクトリにコピーされ、プログラムの実行中に使われます。 パッケージ作成者によっては、パッケージ コンシューマーがコンパイル時に使用できるものをアセンブリ (またはアセンブリのサブセット) のみにしたい場合があります。ただし、実行時にはすべての依存関係を提供する必要があります。 このドキュメントでは、このような結果を達成するための方法について説明します。

1 つのアセンブリに 1 つのパッケージを用意し、他のアセンブリへのパッケージの依存関係を設定することをお勧めします。 NuGet を使ってプロジェクトを復元する場合、資産の選択が実行されます。また、さまざまな資産クラスの包含、除外、非公開の設定がサポートされます。 パッケージを使うすべてのユーザーがパッケージの依存関係をコンパイル時の資産として指定できないようにするには、compile 資産を非公開にします。 こうすると、生成されるパッケージでは依存関係から compile が除外されます。 何も指定しない場合の既定の非公開資産は contentfiles;build;analyzers であることに注意してください。 そのため、PackageReference または ProjectReference には PrivateAssets="compile;contentfiles;build;analyzers" を使うようにします。

<ItemGroup>
  <ProjectReference Include="..\OtherProject\OtherProject.csproj" PrivateAssets="compile;contentfiles;build;analyzers" />
  <PackageReference Include="SomePackage" Version="1.2.3" PrivateAssets="compile;contentfiles;build;analyzers" />
</ItemGroup>

NuGet に自動生成させるのではなく、カスタムの nuspec ファイルからパッケージを作成する場合、nuspec には exclude XML 属性を使うようにします。

<dependencies>
  <group targetFramework=".NETFramework4.8">
    <dependency id="OtherProject" version="3.2.1" exclude="Compile,Build,Analyzers" />
    <dependency id="SomePackage" version="1.2.3" exclude="Compile,Build,Analyzers" />
  </group>
</dependencies>

これが推奨されるソリューションである理由は 3 つあります。

まず、便利なアセンブリは、新しいアセンブリやパッケージから参照されることがよくあります。 現時点でユーティリティ アセンブリが使われるのが 1 つのパッケージのみの場合、1 つのパッケージで両方のアセンブリをリリースしたくなるかもしれませんが、将来的に 2 つ目のパッケージで "非公開" のユーティリティ アセンブリを使いたくなった場合、ユーティリティ アセンブリを新しいパッケージに移動し、古いパッケージを更新して依存関係として宣言するか、ユーティリティ パッケージを既存のパッケージと新しいパッケージの両方でリリースする必要が出てきます。 アセンブリを 2 つの異なるパッケージでリリースし、プロジェクトから両方のパッケージを参照した場合、2 つのパッケージに異なるバージョンのユーティリティ アセンブリが存在すると、NuGet を使ってバージョン管理を支援できなくなります。

次に、パッケージを使う開発者が、依存関係の API も使いたい場合があります。 たとえば、パッケージ Microsoft.ServiceHub.Client バージョン 3.0.3078 について考えてみます。 このパッケージをダウンロードし、nuspec ファイルを確認すると、Microsoft.VisualStudio. で始まる 2 つのパッケージが依存関係として登録されていることを確認できます。つまり、それらは実行時に必要ですが、除外されているコンパイル資産もあります。 これは、Microsoft.ServiceHub.Client を使うプロジェクトは、プロジェクトでこれらのパッケージを明示的にインストールしない限り、IntelliSense で、またはプロジェクトのビルド時に Visual Studio API を使用できないことを意味します。 そして、これは除外資産があるパッケージ依存関係の利点です。 パッケージを使っているプロジェクトで、依存関係も使いたい場合は、パッケージへの参照を追加して、API を使用できるようにすることができます。

最後に、パッケージに複数のアセンブリが含まれ、パッケージが複数のターゲット フレームワークをサポートしている場合に、NuGet のアセンブリ選択について過去に混乱した経験があるパッケージ作成者もいます。 メイン アセンブリがユーティリティ アセンブリと異なるターゲット フレームワークをサポートする場合、すべてのアセンブリをどの lib/ ディレクトリに配置するかが明確ではない場合があります。 各パッケージをアセンブリ名で分けることで、各アセンブリを格納する lib/ フォルダーがより直感的になります。 これは Package1.net48Package1.net6.0 のパッケージを用意するという意味ではないことに注意してください。 lib/net48/Package1.dlllib/net6.0/Package6.0Package1 に、lib/netstandard2.0/Package2.dlllib/net5.0/Package2.dllPackage2 に用意するという意味です。 Nuget を使ってプロジェクトを復元する場合、Nuget から 2 つのパッケージに対して独立して資産の選択が実行されます。

また、依存関係の包含または除外資産は、PackageReference を使うプロジェクトからのみ使われることに注意してください。 プロジェクトで packages.config を使ってパッケージをインストールした場合、依存関係がインストールされ、その API も利用できるようになります。 packages.config は Visual Studio の以前の .NET Framework プロジェクト テンプレートでのみサポートされています。 SDK スタイルのプロジェクトは、.NET Framework をターゲットにしている場合でも、packages.config をサポートしていないため、依存関係の包含または除外資産をサポートしています。

PackageReferencepackages.config では、使用できる機能が異なります。 PackageReferencepackages.config、またはその両方を使うパッケージ コンシューマーをサポートするかどうかで、パッケージの作成方法が変わります。

NuGet の MSBuild パックのターゲットは、パッケージ内にプロジェクト参照を自動的に含めることをサポートしていません。 パッケージの依存関係として参照されるプロジェクトが一覧表示されるだけです。 コミュニティ メンバーがこの結果を達成する方法を共有した場合、GitHub には問題があります。通常、この場合、パッケージに含まれる内容に関するドキュメントの説明に従って、PackagePath MSBuild 項目のメタデータを使ってパッケージ内の任意の場所にファイルを配置し、プロジェクトの参照がパッケージの依存関係にならないように SuppressDependenciesWhenPacking を使う必要があります。 また、この機能をサポートし、NuGet の公式パックの代わりとして使用できる、コミュニティが開発したツールもあります。

PackageReference サポート

パッケージ コンシューマーが PackageReference を使うと、前述のように NuGet によってコンパイルと実行時の資産が独立して選ばれます。

コンパイル資産には ref/<tfm>/*.dll が優先されます (たとえば ref/net6.0/*.dll) が、存在しない場合は lib/<tfm>/*.dll にフォールバックします (たとえば lib/net6.0/*.dll)。

実行時の資産には runtimes/<rid>/lib/<tfm>/*.dll が優先されます (たとえば (runtimes/win11-x64/lib/net6.0/*.dll)) が、存在しない場合は lib/<tfm>/*.dll にフォールバックします。

ref\<tfm>\ のアセンブリは実行時に使用されないため、パッケージ サイズを減らすメタデータのみのアセンブリになることがあります。

packages.config サポート

packages.config を使用して NuGet パッケージを管理するプロジェクトでは通常、lib\<tfm>\ ディレクトリのすべてのアセンブリに参照が追加されます。 ref\ ディレクトリは PackageReference をサポートする目的で追加されており、packages.config の使用時には考慮されません。 packages.config を使用するプロジェクトで参照されるアセンブリを明示的に設定するには、パッケージで nuspec ファイルの <references> 要素を使用する必要があります。 次に例を示します。

<references>
    <group targetFramework="net45">
        <reference file="MyLibrary.dll" />
    </group>
</references>

MSBuild パックのターゲットは <references> 要素をサポートしていません。 MSBuild パックを使う場合は、.nuspec ファイルを使ったパックに関するドキュメントを参照してください。

Note

packages.config プロジェクトでは、ResolveAssemblyReference というプロセスを使用し、bin\<configuration>\ 出力ディレクトリにアセンブリをコピーします。 プロジェクトのアセンブリがコピーされると、ビルド システムがアセンブリ マニフェストで参照アセンブリを探し、そのアセンブリをコピーし、すべてのアセンブリに再帰的に繰り返します。 つまり、いずれかのアセンブリがリフレクションによってのみ読み込まれた場合 (Assembly.Load、MEF、または他の依存関係の挿入フレームワーク)、bin\<tfm>\ に存在する場合でも、プロジェクトの bin\<configuration>\ 出力ディレクトリにコピーされない可能性があります。 また、P/Invoke を使って呼び出されたネイティブ コードではなく、.NET アセンブリに対してのみ機能することを意味します。

PackageReferencepackages.config の両方のサポート

重要

あるパッケージに nuspec <references> 要素が含まれるが、ref\<tfm>\ のアセンブリが含まれない場合、NuGet では、コンパイル アセットとランタイム アセットの両方として、nuspec <references> 要素に一覧表示されるアセンブリがアドバタイズされます。 つまり、参照されるアセンブリで lib\<tfm>\ ディレクトリに他のアセンブリを読み込む必要があるとき、ランタイム例外が発生します。 そのため、packages.config のサポートのために nuspec <references> を使い、PackageReference のサポートのために ref/ フォルダーにもアセンブリを重複して配置することが重要です。 runtimes/ パッケージ フォルダーを使う必要はありませんが、念のため前述のセクションに追加しました。

パッケージには、.NET Framework 4.7.2 を対象とする 3 つのアセンブリ (MyLib.dllMyHelpers.dllMyUtilities.dll) が含まれます。 MyUtilities.dll には、他の 2 つのアセンブリでのみ使用されるクラスが含まれます。そのため、IntelliSense やコンパイルで、自分のパッケージを使用するプロジェクトでそれらのクラスを利用できるようにします。 nuspec ファイルには、次の XML 要素を含める必要があります。

<references>
    <group targetFramework="net472">
        <reference file="MyLib.dll" />
        <reference file="MyHelpers.dll" />
    </group>
</references>

パッケージの内容が次のとおりであることを確認する必要があります。

lib\net472\MyLib.dll
lib\net472\MyHelpers.dll
lib\net472\MyUtilities.dll
ref\net472\MyLib.dll
ref\net472\MyHelpers.dll