.NET での依存関係の挿入の基礎について理解する
この記事では、ServiceCollection とそれに対応する ServiceProvider を手動で作成する .NET コンソール アプリを作成します。 依存関係の挿入 (DI) を使用してサービスを登録し解決する方法について学びます。 この記事では、Microsoft.Extensions.DependencyInjection NuGet パッケージを使用して、.NET の DI の基礎を示します。
Note
この記事では、Generic Host 機能を利用しません。 より包括的なガイドについては、「.NET で依存関係の挿入を使用する」をご覧ください。
作業の開始
開始するには、DI.Basics という名前の新しい .NET コンソール アプリケーションを作成します。 コンソール プロジェクトを作成するための最も一般的な方法の一部を次の一覧で参照してください。
- Visual Studio: [ファイル] > [新規作成] > [プロジェクト] メニュー。
- Visual Studio Code と C# Dev Kit 拡張機能: [ソリューション エクスプローラー] メニュー オプション。
- .NET CLI: ターミナルの
dotnet new console
コマンド。
プロジェクト ファイルの Microsoft.Extensions.DependencyInjection にパッケージ参照を追加する必要があります。 この方法に関係なく、プロジェクトが DI.Basics.csproj ファイルの次に示す XML のようになっていることを確認します。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup>
</Project>
依存関係の挿入の基礎
依存関係の挿入は、ハードコーディングされた依存関係を削除し、アプリケーションの保守性とテスト性を高める設計パターンです。 DI は、クラスとその依存関係の間で Inversion of Control (IoC) を実現するための手法です。
.NET の DI の抽象クラスは、Microsoft.Extensions.DependencyInjection.Abstractions NuGet パッケージで定義されています。
- IServiceCollection: サービス記述子のコレクションのコントラクトを指定します。
- IServiceProvider: サービス オブジェクトを取得するためのメカニズムを定義します。
- ServiceDescriptor: サービスの種類、実装、および有効期間を記述します。
.NET では、DI は IServiceCollection
でサービスを追加し構成することによって管理されます。 サービスが登録された後、BuildServiceProvider メソッドを呼び出すことで IServiceProvider
インスタンスがビルドされます。 IServiceProvider
は、登録されているすべてのサービスのコンテナーとして機能し、サービスの解決に使用されます。
exampleのサービスを作成する
すべてのサービスが等しく作成されるわけではありません。 一部のサービスは、サービス コンテナーが取得するたびに新しいインスタンスを必要とします (transient)。一方で他のサービスは、要求間で共有される (scoped) か、アプリの有効期間全体にわたって共有される必要があります (singleton)。 サービスの有効期間の詳細については、「サービスの有効期間」をご覧ください。
同様に、一部のサービスでは具象型のみを公開しますが、他のサービスではインターフェイスと実装型の間のコントラクトとして表されます。 これらの概念を示すのに役立つサービスのバリエーションをいくつか作成します。
"IConsole.cs" という名前の新しい C# ファイルを作成し、次のコードを追加します。
public interface IConsole
{
void WriteLine(string message);
}
このファイルは、1 つのメソッド WriteLine
を公開する IConsole
インターフェイスを定義します。 次に、DefaultConsole.cs という名前の新しい C# ファイルを作成し、次のコードを追加します。
internal sealed class DefaultConsole : IConsole
{
public bool IsEnabled { get; set; } = true;
void IConsole.WriteLine(string message)
{
if (IsEnabled is false)
{
return;
}
Console.WriteLine(message);
}
}
上記のコードは、IConsole
インターフェイスの既定の実装を表しています。 WriteLine
メソッドは、IsEnabled
プロパティに基づいて条件付きでコンソールに書き込みます。
ヒント
実装の名前付けは、開発チームが同意する必要がある選択です。 Default
プレフィックスは、インターフェイスの "既定の" 実装を示す一般的な規則ですが、必須 "ではありません"。
次に、IGreetingService.cs ファイルを作成し、次の C# コードを追加します。
public interface IGreetingService
{
string Greet(string name);
}
次に、DefaultGreetingService.cs という名前の新しい C# ファイルを追加し、次のコードを追加します。
internal sealed class DefaultGreetingService(
IConsole console) : IGreetingService
{
public string Greet(string name)
{
var greeting = $"Hello, {name}!";
console.WriteLine(greeting);
return greeting;
}
}
上記のコードは、IGreetingService
インターフェイスの既定の実装を表しています。 サービスの実装には、プライマリ コンストラクター パラメーターとして IConsole
が必要です。 Greet
メソッド:
- 与えられた
name
でgreeting
を作成します。 IConsole
インスタンスでWriteLine
メソッドを呼び出します。- 呼び出し元に
greeting
を返します。
作成する最後のサービスは "FarewellService.cs" ファイルです。続行する前に、次の C# コードを追加します。
public class FarewellService(IConsole console)
{
public string SayGoodbye(string name)
{
var farewell = $"Goodbye, {name}!";
console.WriteLine(farewell);
return farewell;
}
}
FarewellService
は、インターフェイスではなく具象型を表します。 コンシューマーがアクセスできるように、public
として宣言する必要があります。 internal
および sealed
として宣言された他のサービス実装型とは異なり、このコードは、すべてのサービスがインターフェイスでなければならないことを示しています。 また、サービス実装を sealed
で継承を防ぎ、internal
でアセンブリへのアクセスを制限できることを示しています。
Program
クラスを更新する
"Program.cs" ファイルを開き、既存のコンテンツを次の C# コードに置き換えます。
using Microsoft.Extensions.DependencyInjection;
// 1. Create the service collection.
var services = new ServiceCollection();
// 2. Register (add and configure) the services.
services.AddSingleton<IConsole>(
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
});
services.AddSingleton<IGreetingService, DefaultGreetingService>();
services.AddSingleton<FarewellService>();
// 3. Build the service provider from the service collection.
var serviceProvider = services.BuildServiceProvider();
// 4. Resolve the services that you need.
var greetingService = serviceProvider.GetRequiredService<IGreetingService>();
var farewellService = serviceProvider.GetRequiredService<FarewellService>();
// 5. Use the services
var greeting = greetingService.Greet("David");
var farewell = farewellService.SayGoodbye("David");
上記の更新されたコードは、次の方法を示しています。
- 新しい
ServiceCollection
インスタンスを作成します。 ServiceCollection
でサービスを登録し構成します。- 実装ファクトリ オーバーロードを使用する
IConsole
は、IsEnabled
が 'true に設定されたDefaultConsole
型を返します。 IGreetingService
は、DefaultGreetingService
型の対応する実装型で追加されます。FarewellService
は具象型として追加されます。
- 実装ファクトリ オーバーロードを使用する
ServiceCollection
からServiceProvider
をビルドします。IGreetingService
サービスとFarewellService
サービスを解決します。- 解決されたサービスを使用して、
David
という名前の人に挨拶し、別れを告げます。
DefaultConsole
の IsEnabled
プロパティを false
に更新すると、Greet
メソッドと SayGoodbye
メソッドではコンソールへの結果のメッセージ書き込みが省略されます。 このような変更は、IConsole
サービスが、アプリの動作に影響を与える "依存関係" として、IGreetingService
サービスと FarewellService
サービスに "挿入" されることを示すのに役立ちます。
これらのサービスはすべてシングルトンとして登録されますが、このサンプルでは、transientまたはscoped サービスとして登録された場合も同じように機能します。
重要
この工夫されたexampleでは、サービスの有効期間は関係ありませんが、実際のアプリケーションでは、各サービスの有効期間を慎重に検討する必要があります。
サンプル アプリを実行する
サンプル アプリを実行するには、Visual Studio、Visual Studio Code で F5 キーを押すか、ターミナルで dotnet run
コマンドを実行します。 アプリが完了すると、次の出力が表示されます。
Hello, David!
Goodbye, David!
サービス記述子
ServiceCollection
にサービスを追加するために最も一般的に使用される API は、次のような有効期間名のジェネリック拡張メソッドです。
AddSingleton<TService>
AddTransient<TService>
AddScoped<TService>
これらのメソッドは、ServiceDescriptor インスタンスを作成し ServiceCollection
に追加する便利なメソッドです。 ServiceDescriptor
は、サービスの種類、実装の種類、有効期間を持つサービスを記述する単純なクラスです。 また、実装ファクトリとインスタンスを記述することもできます。
ServiceCollection
に登録した各サービスについて、代わりに ServiceDescriptor
インスタンスで Add
メソッドを直接呼び出します。 次に例を示します。
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IConsole),
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
},
lifetime: ServiceLifetime.Singleton));
上記のコードは、IConsole
サービスが ServiceCollection
に登録された方法と同じです。 Add
メソッドは、IConsole
サービスを記述する ServiceDescriptor
インスタンスを追加するために使用されます。 静的メソッド ServiceDescriptor.Describe
は、さまざまな ServiceDescriptor
コンストラクターにデリゲートします。 IGreetingService
サービスの同等のコードを考えてみましょう。
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IGreetingService),
implementationType: typeof(DefaultGreetingService),
lifetime: ServiceLifetime.Singleton));
上記のコードでは、IGreetingService
サービスとそのサービスの種類、実装の種類、有効期間について説明します。 最後に、FarewellService
サービスの同等のコードを検討します。
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(FarewellService),
implementationType: typeof(FarewellService),
lifetime: ServiceLifetime.Singleton));
上記のコードでは、具体的な FarewellService
型をサービス型と実装型の両方として記述します。 サービスはsingleton サービスとして登録されます。
関連項目
.NET