導入
レッスン15でTaskServiceにITaskRepositoryをコンストラクタで注入しました(コンストラクタインジェクション。詳しくはC#デザインパターンコースのレッスン19)。ただしアプリの依存が増えてくると、「誰が何をnewして、どの順で渡すか」を手であちこち書くのは大変になります。ASP.NET Coreなどが標準採用しているDIコンテナは、この組み立てを自動化する道具です。
説明
flowchart LR
R["登録<br/>services.AddSingleton<ITaskRepository, InMemoryTaskRepository>()"] --> C["コンテナ<br/>IServiceProvider"]
C -->|解決| G["provider.GetRequiredService<TaskListViewModel>()"]
G --> V["依存が全部組み立てられた<br/>TaskListViewModel"]
style C fill:#e1f5fe
Microsoft.Extensions.DependencyInjection(NuGetパッケージ)を使うと、次のように書けます。
// App.xaml.cs や Program.cs の起動処理(読み物:NuGetが必要でブラウザでは実行できません)
var services = new ServiceCollection();
// 登録: 「この抽象が要求されたら、この実装を使う」を宣言するだけ
services.AddSingleton<ITaskRepository, InMemoryTaskRepository>();
services.AddSingleton<ITaskService, TaskService>();
services.AddTransient<TaskListViewModel>();
var provider = services.BuildServiceProvider();
// 解決: TaskListViewModel が要求する ITaskService も、
// ITaskService が要求する ITaskRepository も、まとめて自動で組み立てられる
var viewModel = provider.GetRequiredService<TaskListViewModel>();
- コンテナは各クラスのコンストラクタを見て、必要な依存を再帰的に解決する。
TaskListViewModelがITaskServiceを、ITaskService(の実体TaskService)がITaskRepositoryを要求していることまで自動で辿ってくれる Add系のメソッドは寿命(ライフタイム)も決める- 手で
newを書く場合との違いは「自動化」だけで、DIそのものの考え方(抽象への依存)は変わらない
このサイトのブラウザ環境にはNuGetが無いため、次のレッスンでは同じ考え方を手動で体験します。
まとめ
- DIコンテナは「登録(この抽象にはこの実装)→ 解決(コンストラクタを辿って自動で組み立てる)」を自動化する道具
- ライフタイムには
Singleton(アプリ全体で共有)・Transient(毎回新規)・Scoped(スコープ単位)がある - コンテナが無くても、DIという設計そのもの(抽象への依存・コンストラクタインジェクション)は変わらない
次回: 実際に依存を手で組み立てる場所、Composition Rootです。