.NET Core アプリケーションの開発において、依存関係注入(Dependency Injection:DI)はモジュール性を高めるための標準的なアプローチとなっています。このパターンを採用することで、コンポーネント間の結合を緩やかにし、コードベースの保守性や拡張性を向上させることが可能です。
制御の反転とアーキテクチャ上の利点
DI の背後にある主要な概念は「制御の反転(Inversion of Control:IoC)」です。従来の設計ではクラス内部で依存オブジェクトを实例化していましたが、DI ではオブジェクトの生成とライフサイクル管理を外部のコンテナに委ねます。
このアプローチを採用することにより、以下の恩恵が得られます。
- 結合度の低減:コンポーネント同士が具体的な実装に依存しなくなります。
- テスト容易性の向上:モックオブジェクトを注入することで、単位テストを簡易に実行できます。
- 実行時の変更可能:設定に応じて実装クラスを動的に切り替えることが容易になります。
組み込みコンテナとサービスライフサイクル
.NET Core には、依存関係の登録と解決を行う軽量なコンテナ(IServiceProvider)が標準で搭載されています。サービス登録時には、インスタンスの生存期間を定義する以下の 3 つのライフサイクルから選択します。
- Transient(瞬态):要求されるたびに新しいインスタンスが生成されます。
- Scoped(スコープ付き):特定の範囲(例:HTTP リクエスト)内で単一のインスタンスを共有します。
- Singleton(シングルトン):アプリケーションの起動から終了まで、たった 1 つのインスタンスのみが存在します。
builder.Services.AddTransient<IEmailNotifier, SmtpNotifier>();
builder.Services.AddScoped<IDbContext, AppDbContext>();
builder.Services.AddSingleton<ICacheService, MemoryCache>();
それぞれのライフサイクルは用途に応じて使い分けられます。状態を持たない軽量な処理には Transient が、リクエスト単位で状態を保持が必要なデータベース接続などには Scoped が、アプリケーション全体で共有すべき設定やキャッシュ管理には Singleton が適しています。
実装プロセス
1. コンテナへの登録
.NET 6 以降のプロジェクトでは、通常 Program.cs 内でサービス登録を行います。インターフェースとその実装クラスを関連付けてコンテナに追加します。
var builder = WebApplication.CreateBuilder(args);
// カスタムサービスの登録
builder.Services.AddScoped<IStockManager, StockManager>();
var app = builder.Build();
2. 依存関係の解決
サービスを利用するクラスでは、コンストラクタ経由で必要な依存関係を宣言します。フレームワークが起動時に適切な実装を自動的に注入します。
public class InventoryController : ControllerBase
{
private readonly IStockManager _stockManager;
public InventoryController(IStockManager stockManager)
{
_stockManager = stockManager;
}
[HttpGet("status")]
public IActionResult GetStockStatus()
{
var count = _stockManager.CheckCurrentLevel();
return Ok(new { Stock = count });
}
}
この仕組みにより、InventoryController は IStockManager の具体的な実装details を知る必要なく、インターフェースを通じて機能を利用できます。