前回は、クラスライブラリを作成して別のアプリケーションから利用する流れを見ました。
今回は、ライブラリを NuGet パッケージとして扱う方法、アプリケーションの発行、そして .NET が実行時にアセンブリをどのように探すのかを整理します。
NuGet とは
NuGet は、.NET のパッケージ管理の仕組みです。
外部ライブラリを利用するとき、多くの場合 NuGet パッケージとして参照します。
また、自分で作ったクラスライブラリを社内や公開リポジトリに配布する場合も、NuGet パッケージにできます。
NuGet パッケージには、主に次のような情報が含まれます。
- ライブラリ本体の DLL
- 対象フレームワークごとの成果物
- パッケージ名とバージョン
- 作者、説明、ライセンスなどのメタデータ
- 依存パッケージの情報
ライブラリをパッケージ化する
クラスライブラリのプロジェクトファイルに、パッケージ情報を追加します。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<PackageId>MyCompany.TextTools</PackageId>
<Version>1.0.0</Version>
<Authors>MyCompany</Authors>
<Description>Text formatting utilities.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
</Project>
パッケージを作成します。
dotnet pack -c Release
成功すると、bin/Release 配下に .nupkg ファイルが生成されます。
MyCompany.TextTools.1.0.0.nupkg
このファイルを NuGet のフィードへ公開すると、他のプロジェクトから参照できるようになります。
パッケージを参照する
NuGet パッケージを参照するには、dotnet add package を使います。
dotnet add package MyCompany.TextTools --version 1.0.0
プロジェクトファイルには、次のような参照が追加されます。
<ItemGroup>
<PackageReference Include="MyCompany.TextTools" Version="1.0.0" />
</ItemGroup>
プロジェクト参照はソースコード上の別プロジェクトを参照します。
パッケージ参照は、ビルド済みのパッケージを依存関係として参照します。
開発中はプロジェクト参照、配布後はパッケージ参照、という使い分けがよくあります。
ローカルのパッケージ置き場を使う
公開フィードへ登録する前に、ローカルフォルダーをパッケージ置き場として使えます。
mkdir local-packages
dotnet pack TextTools/TextTools.csproj -c Release -o local-packages
利用側でパッケージソースを指定します。
dotnet add package MyCompany.TextTools \
--version 1.0.0 \
--source ./local-packages
社内ライブラリや検証中のパッケージでは、この方法が便利です。
アプリケーションを発行する
dotnet publish を使うと、実行に必要なファイルを発行用フォルダーへまとめられます。
dotnet publish -c Release -o publish
publish フォルダーには、アプリケーション本体、依存 DLL、設定ファイルなどが出力されます。
ビルドは開発者向けの成果物を作る処理です。
発行は、実行環境へ配置するための成果物を作る処理です。
フレームワーク依存の発行
フレームワーク依存の発行では、実行先に .NET ランタイムがインストールされている前提になります。
dotnet publish -c Release -o publish
特徴は次の通りです。
- 出力サイズが比較的小さい
- 実行先に対象バージョンの .NET ランタイムが必要
- ランタイム更新の恩恵を受けやすい
サーバーや開発者向けツールなど、実行環境を管理できる場合に向いています。
自己完結型の発行
自己完結型の発行では、.NET ランタイムも含めて配布します。
dotnet publish -c Release \
-r win-x64 \
--self-contained true \
-o publish-win
特徴は次の通りです。
- 実行先に .NET ランタイムがなくても動かせる
- 出力サイズが大きくなる
- OS や CPU アーキテクチャごとに発行が必要
利用者の環境に .NET が入っているかわからない配布物では便利です。
Linux 向けなら、たとえば次のように発行します。
dotnet publish -c Release \
-r linux-x64 \
--self-contained true \
-o publish-linux
単一ファイルとして発行する
実行ファイルを 1 つにまとめたい場合は、単一ファイル発行もできます。
dotnet publish -c Release \
-r win-x64 \
--self-contained true \
-p:PublishSingleFile=true \
-o publish-single
配布しやすくなりますが、すべてのシナリオで完全に 1 ファイルだけになるとは限りません。
ネイティブ依存関係や設定ファイルの扱いには注意が必要です。
.NET がアセンブリを見つける仕組み
.NET アプリケーションは、実行時に必要なアセンブリを読み込みます。
基本的には、発行フォルダーや依存関係情報をもとに、必要な DLL を探します。
SDK スタイルのプロジェクトでは、ビルドや発行時に依存関係が整理され、実行に必要なファイルが出力先へコピーされます。
実行時のアセンブリ探索には、次のような情報が関係します。
- アプリケーションの出力フォルダー
.deps.jsonに記録された依存関係- NuGet パッケージキャッシュ
- 既定の読み込みコンテキスト
- カスタムの読み込みコンテキスト
通常のアプリケーションでは、これらを直接制御する必要はあまりありません。
一方、プラグインを動的に読み込む場合は、AssemblyLoadContext を使って明示的に解決することがあります。
読み込み済みアセンブリを確認する
実行時にどのアセンブリが読み込まれているかは、次のように確認できます。
using System.Reflection;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies.OrderBy(a => a.GetName().Name))
{
Console.WriteLine(assembly.FullName);
Console.WriteLine($" {assembly.Location}");
}
依存関係の調査や、想定外のバージョンが読み込まれていないかを確認するときに役立ちます。
まとめ
NuGet は、.NET ライブラリを配布・参照するための標準的な仕組みです。
クラスライブラリをパッケージ化すれば、複数のプロジェクトからバージョン管理しながら利用できます。
また、dotnet publish を使うと、アプリケーションを実行環境へ配置するための成果物を作れます。
フレームワーク依存型は軽量で、自己完結型はランタイム込みで配布できる、という違いがあります。
アセンブリ探索の詳細は普段あまり意識しませんが、動的読み込みやプラグイン構成を扱うときには重要になります。