次の方法で共有


クイック スタート: .NET での依存関係の挿入の基本

このクイック スタートでは、 ServiceCollection とそれに対応する ServiceProviderを手動で作成する .NET コンソール アプリを作成します。 依存関係の挿入 (DI) を使用してサービスを登録して解決する方法について説明します。 この記事では、 Microsoft.Extensions.DependencyInjection NuGet パッケージを使用して、.NET の DI の基本を示します。

この記事では、 汎用ホスト 機能を利用しません。 より包括的なガイドについては、「 .NET での依存関係の挿入の使用」を参照してください。

概要

開始するには、 DI.Basics という名前の新しい .NET コンソール アプリケーションを作成します。 コンソール プロジェクトを作成するための最も一般的な方法の一部を次の一覧で参照します。

プロジェクト ファイル内の Microsoft.Extensions.DependencyInjection にパッケージ参照を追加する必要があります。 方法に関係なく、プロジェクトが DI.Basics.csproj ファイルの次の XML に似ているようにします。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
  </ItemGroup>

</Project>

依存関係の挿入の基礎

依存関係の挿入は、ハードコーディングされた依存関係を削除し、アプリケーションの保守性とテスト性を高める設計パターンです。 DI は、クラスとその依存関係 の間で制御の反転 (IoC) を実現するための手法です。

.NET の DI の抽象化は、 Microsoft.Extensions.DependencyInjection.Abstractions NuGet パッケージで定義されています。

  • IServiceCollection: サービス記述子のコレクションのコントラクトを定義します。
  • IServiceProvider: サービス オブジェクトを取得するためのメカニズムを定義します。
  • ServiceDescriptor: サービスの種類、実装、有効期間を持つサービスについて説明します。

.NET では、DI はサービスを追加し、 IServiceCollectionで構成することによって管理されます。 サービスが登録されると、IServiceProvider メソッドを呼び出すことによって、BuildServiceProvider インスタンスがビルドされます。 IServiceProviderは、登録されているすべてのサービスのコンテナーとして機能し、サービスの解決に使用されます。

サンプル サービスを作成する

すべてのサービスが均等に作成されるわけではありません。 一部のサービスでは、サービス コンテナーがそれらを取得するたびに新しいインスタンスが必要になります (一時的)。他のサービスは、要求間で共有する (スコープ指定された) か、アプリの有効期間全体 (シングルトン) で共有する必要があります。 サービスの有効期間の詳細については、「サービスの 有効期間」を参照してください。

同様に、一部のサービスは具象型のみを公開し、他のサービスはインターフェイスと実装型の間のコントラクトとして表されます。 これらの概念を示すのに役立つサービスのバリエーションをいくつか作成します。

IConsole.csという名前 新しい C# ファイルを作成し、次のコードを追加します。

public interface IConsole
{
    void WriteLine(string message);
}

このファイルは、IConsole 1 つのメソッドを公開するWriteLine インターフェイスを定義します。 次に、 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 メソッド:

  • greetingを指定してnameを作成します。
  • WriteLine インスタンスでIConsole メソッドを呼び出します。
  • 呼び出し元に 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は、DefaultConsoleIsEnabledに設定されたtrue型を返します。
    • IGreetingServiceは、DefaultGreetingService型の対応する実装型で追加されます。
    • FarewellServiceは具象型として追加されます。
  • ServiceProviderからServiceCollectionをビルドします。
  • IGreetingServiceサービスとFarewellService サービスを解決します。
  • 解決されたサービスを使用して、 Davidという名前の人に挨拶し、別れを告げる。

IsEnabledDefaultConsole プロパティをfalseに更新すると、Greetメソッドと SayGoodbye メソッドでは、結果のメッセージへのコンソールへの書き込みが省略されます。 このような変更は、IConsole サービスがそのアプリの動作に影響を与える依存関係としてIGreetingServiceおよびFarewellService サービスに挿入されることを示すのに役立ちます。

これらのサービスはすべてシングルトンとして登録されますが、このサンプルでは、 一時的 なサービスまたは スコープ付 きサービスとして登録された場合も同じように機能します。

Important

この工夫された例では、サービスの有効期間は関係ありませんが、実際のアプリケーションでは、各サービスの有効期間を慎重に検討する必要があります。

サンプル アプリを実行する

サンプル アプリを実行するには、Visual Studio または Visual Studio Code で F5 キーを押すか、ターミナルで dotnet run コマンドを実行します。 アプリが完了すると、次の出力が表示されます。

Hello, David!
Goodbye, David!

サービス記述子

ServiceCollectionにサービスを追加するために最も一般的に使用される API は、次のような有効期間名のジェネリック拡張メソッドです。

  • AddSingleton<TService>
  • AddTransient<TService>
  • AddScoped<TService>

これらのメソッドは、 ServiceDescriptor インスタンスを作成して ServiceCollectionに追加する便利なメソッドです。 ServiceDescriptorは、サービスの種類、実装の種類、有効期間を持つサービスを記述する単純なクラスです。 また、実装ファクトリとインスタンスについても説明できます。

ServiceCollectionに登録した各サービスについて、代わりに Add インスタンスで ServiceDescriptor メソッドを直接呼び出します。 次の例を考えてみましょう。

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IConsole),
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    },
    lifetime: ServiceLifetime.Singleton));

上記のコードは、 IConsole サービスが ServiceCollectionに登録された方法と同じです。 Add メソッドは、ServiceDescriptor サービスを記述するIConsole インスタンスを追加するために使用されます。 静的メソッドは、さまざまなコンストラクター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 型をサービス型と実装型の両方として記述します。 サービスはシングルトン サービスとして登録されます。

こちらも参照ください