.NET では依存関係の挿入 (DI) ソフトウェア設計パターンがサポートされています。これは、クラスとその依存関係の間で制御の反転 (IoC) を実現するための手法です。 .NET での依存関係の挿入は、構成、ログ、オプション パターンと共に、フレームワークの組み込み部分です。
"依存関係" とは、他のオブジェクトが依存するオブジェクトのことです。 他のクラスが依存している、次の MessageWriter メソッドを備えた Write クラスを調べます。
public class MessageWriter
{
    public void Write(string message)
    {
        Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
    }
}
クラスは、MessageWriter メソッドを使用するWrite クラスのインスタンスを作成できます。 次の例で、MessageWriter クラスは Worker クラスの依存関係です。
public class Worker : BackgroundService
{
    private readonly MessageWriter _messageWriter = new();
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
            await Task.Delay(1_000, stoppingToken);
        }
    }
}
このクラスは MessageWriter クラスを作成し、これに直接依存しています。 ハードコーディングされた依存関係には、前の例のような問題があり、次の理由から回避する必要があります。
- 
              
MessageWriterを別の実装に置き換えるには、Workerクラスを変更する必要があります。 - 
              
MessageWriterに依存関係がある場合は、Workerクラスもそれらを構成する必要があります。 複数のクラスがMessageWriterに依存している大規模なプロジェクトでは、構成コードがアプリ全体に分散するようになります。 - このような実装では、単体テストを行うことが困難です。 アプリはモックまたはスタブの 
MessageWriterクラスを使用する必要がありますが、この方法では不可能です。 
依存性注入は次の問題に対処します。
- 依存関係の実装を抽象化するための、インターフェイスまたは基底クラスの使用。
 - サービス コンテナー内の依存関係の登録。 .NET には、組み込みのサービス コンテナー IServiceProvider が用意されています。 通常、サービスは、アプリの起動時に登録され、IServiceCollection に追加されます。 すべてのサービスが追加されたら、 BuildServiceProvider を使用してサービス コンテナーを作成します。
 - サービスを使用するクラスのコンストラクターへの、サービスの "挿入"。 依存関係のインスタンスの作成、およびインスタンスが不要になったときの廃棄の役割を、フレームワークが担当します。
 
たとえば、IMessageWriter インターフェイスに Write メソッドを定義します。
namespace DependencyInjection.Example;
public interface IMessageWriter
{
    void Write(string message);
}
このインターフェイスは、具象型 MessageWriter によって実装されます。
namespace DependencyInjection.Example;
public class MessageWriter : IMessageWriter
{
    public void Write(string message)
    {
        Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
    }
}
このサンプル コードの場合、具象型 IMessageWriter を使用して MessageWriter サービスが登録されます。 
              AddSingleton メソッドでは、シングルトンの有効期間 (アプリの有効期間) でサービスを登録します。 
              サービスの有効期間については、この記事の後半で説明します。
using DependencyInjection.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();
using IHost host = builder.Build();
host.Run();
前のコードでは、サンプル アプリで次の処理が行われます。
ホスト アプリ ビルダーのインスタンスを作成します。
次を登録してサービスを構成します。
- ホステッド サービスとしての 
Worker。 詳細については、「.NET の Worker サービス」を参照してください。 - 
              
IMessageWriterクラスの該当実装があるシングルトン サービスとしてのMessageWriterインターフェイス。 
- ホステッド サービスとしての 
 ホストをビルドして実行します。
ホストには、依存関係挿入サービス プロバイダーが含まれています。 また、Worker のインスタンスを自動的に作成し、対応する IMessageWriter 実装を引数として提供するために必要なその他すべての関連サービスが含まれています。
namespace DependencyInjection.Example;
public sealed class Worker(IMessageWriter messageWriter) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
            await Task.Delay(1_000, stoppingToken);
        }
    }
}
DI パターンを使用することにより、Worker サービスは次のようになります。
- 具象型 
MessageWriterは使用せず、実装するIMessageWriterインターフェイスのみを使用します。 これにより、ワーカー サービスを変更することなく、ワーカー サービスで使用される実装を簡単に変更できます。 - 
              
MessageWriterのインスタンスは作成できません。 DI コンテナーによってインスタンスが作成されます。 
              IMessageWriter インターフェイスの実装は、組み込みのログ記録 API を使用して改善できます。
namespace DependencyInjection.Example;
public class LoggingMessageWriter(
    ILogger<LoggingMessageWriter> logger) : IMessageWriter
{
    public void Write(string message) =>
        logger.LogInformation("Info: {Msg}", message);
}
更新された AddSingleton メソッドでは、新しい IMessageWriter の実装が登録されます。
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
              HostApplicationBuilder (builder) 型は、Microsoft.Extensions.Hosting NuGet パッケージの一部です。
              LoggingMessageWriter は、コンストラクターで要求される ILogger<TCategoryName> によって異なります。 
              ILogger<TCategoryName> は、フレームワークで提供されるサービスです。
依存関係の挿入をチェーン形式で使用することはよくあります。 次に、要求されたそれぞれの依存関係が、それ自身の依存関係を要求します。 コンテナーによってグラフ内の依存関係が解決され、完全に解決されたサービスが返されます。 解決する必要がある依存関係の集合セットは、通常、 依存関係ツリー、 依存関係グラフ、または オブジェクト グラフと呼ばれます。
コンテナーでは、ILogger<TCategoryName>を活用し、すべての (ジェネリック) 構築型を登録する必要をなくすことで、 を解決します。
依存関係の挿入に関する用語では、サービスは次のようになります。
- 通常、他のオブジェクト (
IMessageWriterサービスなど) にサービスを提供するオブジェクトです。 - Web サービスとは関係ありませんが、サービスでは Web サービスが使用される場合があります。
 
フレームワークは、堅牢な ログ システムを備えています。 前の例で示した IMessageWriter 実装は、ログではなく基本的な DI を示しています。 ほとんどのアプリでは、ロガーを記述する必要はありません。 次のコードは、既定のログを使用する方法を示しています。この場合、Worker をホステッド サービス AddHostedService として登録するだけで済みます。
public sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1_000, stoppingToken);
        }
    }
}
上記のコードを使用すると、フレームワークによってログ記録が提供されるため、 Program.csを更新する必要はありません。
複数のコンストラクター検出ルール
型で複数のコンストラクターが定義されている場合、サービス プロバイダーにはどのコンストラクターを使うかを決定するためのロジックがあります。 型が DI 解決可能であるパラメーターを一番多く持つコンストラクターが選ばれます。 次の C# のサービス例を考えてみます。
public class ExampleService
{
    public ExampleService()
    {
    }
    public ExampleService(ILogger<ExampleService> logger)
    {
        // omitted for brevity
    }
    public ExampleService(FooService fooService, BarService barService)
    {
        // omitted for brevity
    }
}
前のコードでは、ログ記録が追加され、サービス プロバイダーから解決できるが、 FooService 型と BarService 型は解決できないと仮定します。 
              ILogger<ExampleService> パラメーターを持つコンストラクターは、ExampleService インスタンスを解決します。 より多くのパラメーターを定義するコンストラクターがある場合でも、 FooService 型と BarService 型は DI 解決できません。
コンストラクターを検出するときにあいまいさがある場合は、例外がスローされます。 次の C# のサービス例を考えてみます。
public class ExampleService
{
    public ExampleService()
    {
    }
    public ExampleService(ILogger<ExampleService> logger)
    {
        // omitted for brevity
    }
    public ExampleService(IOptions<ExampleOptions> options)
    {
        // omitted for brevity
    }
}
警告
DI 解決可能な型パラメーターがあいまいであると、ExampleService コードは例外をスローします。 これを行わないでください。これは、"あいまいな DI 解決可能な型" の意味を示すことを目的としています。
前の例では、3 つのコンストラクターがあります。 最初のコンストラクターにはパラメーターがなく、サービス プロバイダーからのサービスを必要としません。 ログ記録とオプションの両方が DI コンテナーに追加されており、DI 解決可能なサービスであることを想定してください。 DI コンテナーは、 ExampleService 型の解決を試みると、2 つのコンストラクターがあいまいであるため、例外をスローします。
代わりに、両方の DI 解決可能な型を受け入れるコンストラクターを定義することで、あいまいさを回避します。
public class ExampleService
{
    public ExampleService()
    {
    }
    public ExampleService(
        ILogger<ExampleService> logger,
        IOptions<ExampleOptions> options)
    {
        // omitted for brevity
    }
}
拡張メソッドを使用したサービスのグループを登録する
Microsoft 拡張機能には、関連するサービスのグループを登録するための規則が使用されます。 規則は、単一の Add{GROUP_NAME} 拡張メソッドを使用して、フレームワーク機能に必要なすべてのサービスを登録するというものです。 たとえば、AddOptions 拡張メソッドにより、オプションの使用に必要なすべてのサービスが登録されます。
フレームワークが提供するサービス
使用可能なホストまたはアプリ ビルダー パターンのいずれかを利用すると、既定値が適用され、サービスがフレームワークによって登録されます。 最も一般的なホストおよびアプリ ビルダー パターンをいくつか考えてみましょう。
- Host.CreateDefaultBuilder()
 - Host.CreateApplicationBuilder()
 - WebHost.CreateDefaultBuilder()
 - WebApplication.CreateBuilder()
 - WebAssemblyHostBuilder.CreateDefault
 - MauiApp.CreateBuilder
 
これらの API からビルダーを作成した後、 IServiceCollection には、 ホストの構成方法に応じて、フレームワークによって定義されたサービスがあります。 .NET テンプレートをベースにしたアプリの場合、フレームワークで数百単位のサービスを登録できます。
次の表に、フレームワークによって登録されるサービスのごく一部を示します。
サービスの有効期間
サービスは、次のいずれかの有効期間で構成できます。
次のセクションでは、前の有効期間について個別に説明します。 登録される各サービスの適切な有効期間を選択します。
一時的
有効期間が一時的なサービスは、サービス コンテナーから要求されるたびに作成されます。 "一時的なもの" としてサービスを登録するには、 を呼び出します。AddTransient
要求を処理するアプリでは、一時的なサービスが要求の最後に破棄されます。 この有効期間では、サービスが解決され、毎回構築されるため、要求ごとの割り当てが発生します。 詳細については、「依存関係の挿入のガイドライン: 一時的なインスタンスと共有インスタンスのための IDisposable ガイダンス」を参照してください。
スコープ
Web アプリケーションの場合、スコープ付き有効期間は、クライアント要求 (接続) ごとにサービスが 1 回作成されることを示します。 AddScoped でスコープ付きサービスを登録します。
要求を処理するアプリでは、スコープ付きサービスは要求の最後で破棄されます。
注
Entity Framework Core を使用する場合、既定では AddDbContext 拡張メソッドによって、スコープ付き有効期間を持つ DbContext 型が登録されます。
スコープ付きサービスは、スコープ内 (暗黙的なスコープ (ASP.NET Core の要求ごとのスコープなど) または IServiceScopeFactory.CreateScope()で作成された明示的なスコープのいずれかから常に使用する必要があります。
コンストラクターの挿入を使用して、またはシングルトン内のから要求することによって、シングルトンからスコープ付きサービスを直接解決。 これにより、スコープ指定されたサービスがシングルトンのように動作し、後続の要求を処理するときに正しくない状態が発生する可能性があります。
シングルトン内でスコープサービスを解決することは、IServiceScopeFactoryで明示的なスコープを作成して使用する場合に許可されています。
次の場合も問題ありません。
- スコープ付きまたは一時的なサービスからシングルトン サービスを解決する。
 - スコープ付きサービスを、別のスコープ付きまたは一時的なサービスから解決する。
 
既定では、開発環境で、より長い有効期間を持つ別のサービスからサービスを解決すると、例外がスローされます。 詳しくは、「スコープの検証」をご覧ください。
シングルトン
シングルトン有効期間サービスが作成されるのは、次のいずれかの場合です。
- それらが初めて要求された場合。
 - 開発者によって、実装インスタンスがコンテナーに直接提供される場合。 このアプローチはほとんど必要ありません。
 
依存関係の挿入コンテナーから送信されるサービス実装の後続の要求すべてには、同じインスタンスが使用されます。 アプリをシングルトンで動作させる必要がある場合は、サービス コンテナーによるサービスの有効期間の管理を許可してください。 シングルトン デザイン パターンを実装したり、シングルトンを破棄するコードを提供したりしないでください。 コンテナーからサービスを解決したコードによって、サービスが破棄されることはありません。 型またはファクトリがシングルトンとして登録されている場合、コンテナーによってシングルトンが自動的に破棄されます。
シングルトン サービスを AddSingleton で登録します。 シングルトン サービスはスレッド セーフである必要があり、ほとんどの場合、ステートレス サービスで使用されます。
要求を処理するアプリでは、アプリのシャットダウン時に ServiceProvider が破棄されるとき、シングルトン サービスが破棄されます。 メモリはアプリがシャットダウンするまで解放されないため、シングルトン サービスでのメモリの使用を検討してください。
サービス登録メソッド
このフレームワークでは、特定のシナリオで役立つサービス登録拡張メソッドが提供されます。
| 方法 | 自動 オブジェクト 破棄  | 
複数 実装  | 
引数を渡す | 
|---|---|---|---|
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()例: services.AddSingleton<IMyDep, MyDep>(); | 
はい | はい | いいえ | 
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})例 : services.AddSingleton<IMyDep>(sp => new MyDep());services.AddSingleton<IMyDep>(sp => new MyDep(99)); | 
はい | はい | はい | 
Add{LIFETIME}<{IMPLEMENTATION}>()例: services.AddSingleton<MyDep>(); | 
はい | いいえ | いいえ | 
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})例 : services.AddSingleton<IMyDep>(new MyDep());services.AddSingleton<IMyDep>(new MyDep(99)); | 
いいえ | はい | はい | 
AddSingleton(new {IMPLEMENTATION})例 : services.AddSingleton(new MyDep());services.AddSingleton(new MyDep(99)); | 
いいえ | いいえ | はい | 
型の廃棄の詳細については、「サービスの破棄」を参照してください。
実装型のみでサービスを登録することは、同じ実装とサービスの型でそのサービスを登録することと同じです。 表すクレソン、ダン橄欖岩製品構文解析木作法:
services.AddSingleton<ExampleService>();
これは、サービスを同じ型のサービスと実装の両方に登録することと同じです。
services.AddSingleton<ExampleService, ExampleService>();
これが同じであることが、明示的なサービス型を使用しないメソッドを使用してサービスの複数の実装を登録できない理由です。 これらのメソッドでは、サービスの複数のインスタンスを登録できますが、すべて同じ実装型になります。
サービス登録メソッドのいずれかを使用して、同じサービスの種類の複数のサービス インスタンスを登録できます。 次の例では、AddSingleton をサービス型として使用して、IMessageWriter を 2 回呼び出します。 2 回目の AddSingleton の呼び出しにより、IMessageWriter として解決された場合は前のものがオーバーライドされ、IEnumerable<IMessageWriter> を介して複数のサービスが解決された場合は前のものに追加されます。 
              IEnumerable<{SERVICE}> を介して解決された場合、サービスは登録された順に表示されます。
using ConsoleDI.IEnumerableExample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
builder.Services.AddSingleton<ExampleService>();
using IHost host = builder.Build();
_ = host.Services.GetService<ExampleService>();
await host.RunAsync();
上記のサンプル ソース コードを使用すると、IMessageWriter の 2 つの実装が登録されます。
using System.Diagnostics;
namespace ConsoleDI.IEnumerableExample;
public sealed class ExampleService
{
    public ExampleService(
        IMessageWriter messageWriter,
        IEnumerable<IMessageWriter> messageWriters)
    {
        Trace.Assert(messageWriter is LoggingMessageWriter);
        var dependencyArray = messageWriters.ToArray();
        Trace.Assert(dependencyArray[0] is ConsoleMessageWriter);
        Trace.Assert(dependencyArray[1] is LoggingMessageWriter);
    }
}
              ExampleService により、2 つのコンストラクター パラメーター (1 つの IMessageWriter と IEnumerable<IMessageWriter>) が定義されます。 1 つの IMessageWriter は登録する最後の実装ですが、 IEnumerable<IMessageWriter> はすべての登録済み実装を表します。
フレームワークには TryAdd{LIFETIME} 拡張メソッドも用意されており、実装がまだ登録されていない場合にのみ、サービスが登録されます。
次の例では、AddSingleton の呼び出しによって、ConsoleMessageWriter の実装として IMessageWriter が登録されます。 
              TryAddSingleton の呼び出しでは何も行われません。IMessageWriter には登録された実装が既に含まれているからです。
services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();
              TryAddSingletonは既に追加されており、"try" が失敗しているため、効果はありません。 
              ExampleServiceは、次のようにアサートします。
public class ExampleService
{
    public ExampleService(
        IMessageWriter messageWriter,
        IEnumerable<IMessageWriter> messageWriters)
    {
        Trace.Assert(messageWriter is ConsoleMessageWriter);
        Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
    }
}
詳細については、以下を参照してください:
              TryAddEnumerable(ServiceDescriptor) メソッドでは、"同じ型" の実装がまだ存在しない場合にのみサービスが登録されます。 複数のサービスは、IEnumerable<{SERVICE}> によって解決されます。 サービスを登録するときに、同じ型のいずれかがまだ追加されていない場合は、インスタンスを追加します。 ライブラリの作成者は、コンテナー内の実装の複数のコピーを登録しないようにするため TryAddEnumerable を使用します。
次の例では、TryAddEnumerable の最初の呼び出しで、MessageWriter の実装として IMessageWriter1 が登録されます。 2 番目の呼び出しでは MessageWriter に IMessageWriter2 が登録されます。 3 番目の呼び出しでは何も行われません。IMessageWriter1 には MessageWriter の登録済みの実装が既に含まれているからです。
public interface IMessageWriter1 { }
public interface IMessageWriter2 { }
public class MessageWriter : IMessageWriter1, IMessageWriter2
{
}
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter2, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
サービス登録は、同じ型の複数の実装を登録する場合を除き、順序に依存しません。
              IServiceCollection は ServiceDescriptor オブジェクトのコレクションです。 次の例は、ServiceDescriptor の作成と追加によってサービスを登録する方法を示しています。
string secretKey = Configuration["SecretKey"];
var descriptor = new ServiceDescriptor(
    typeof(IMessageWriter),
    _ => new DefaultMessageWriter(secretKey),
    ServiceLifetime.Transient);
services.Add(descriptor);
組み込みの Add{LIFETIME} メソッドでも同じ方法が使用されます。 たとえば、AddScoped のソース コードをご覧ください。
コンストラクターの挿入の動作
サービスは次を使用して解決できます。
- IServiceProvider
 - 
              ActivatorUtilities $
- コンテナーに登録されていないオブジェクトが作成されます。
 - 一部のフレームワーク機能に使用されます。
 
 
コンストラクターは、依存関係の挿入によって提供されない引数を受け取ることができますが、引数は既定値を割り当てる必要があります。
サービスをIServiceProviderまたはActivatorUtilitiesで解決する場合は、コンストラクターの挿入にパブリックコンストラクターが必要です。
              ActivatorUtilitiesがサービスを解決する場合、コンストラクターの挿入では、適用可能なコンストラクターが 1 つだけ存在する必要があります。 コンストラクターのオーバーロードはサポートされていますが、依存関係の挿入によってすべての引数を設定できるオーバーロードは 1 つしか存在できません。
スコープの検証
アプリが Development 環境で実行されていて、CreateApplicationBuilder を呼び出してホストを構築している場合、既定のサービス プロバイダーが以下を確認するチェックを実行します。
- スコープ付きサービスが、ルート サービス プロバイダーによって解決されない。
 - スコープ付きサービスが、シングルトンに挿入されない。
 
BuildServiceProvider が呼び出されると、ルート サービス プロバイダーが作成されます。 ルート サービス プロバイダーの有効期間は、プロバイダーがアプリで開始されるとアプリの有効期間に対応し、アプリのシャットダウン時には破棄されます。
スコープ サービスは、それを作成したコンテナーによって破棄されます。 ルート コンテナーに作成されたスコープ付きサービスは、アプリのシャットダウン時に、ルート コンテナーによってのみ破棄されるため、サービスの有効期間は実質的にシングルトンに昇格されます。 
              BuildServiceProvider が呼び出されると、サービス スコープの検証がこれらの状況をキャッチします。
スコープのシナリオ
IServiceScopeFactory は常にシングルトンとして登録されますが、IServiceProvider は包含クラスの有効期間に基づいて変化する可能性があります。 たとえば、スコープからサービスを解決し、それらのサービスのいずれかが IServiceProviderを受け取る場合、それはスコープ付きインスタンスです。
IHostedServiceなど、BackgroundServiceの実装内でスコープ サービスを実現するには、コンストラクターの挿入を介してサービスの依存関係を挿入しないでください。 代わりに、IServiceScopeFactory を挿入し、スコープを作成し、そしてそのスコープから依存関係を解決して適切なサービス有効期間を使用します。
namespace WorkerScope.Example;
public sealed class Worker(
    ILogger<Worker> logger,
    IServiceScopeFactory serviceScopeFactory)
    : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            using (IServiceScope scope = serviceScopeFactory.CreateScope())
            {
                try
                {
                    logger.LogInformation(
                        "Starting scoped work, provider hash: {hash}.",
                        scope.ServiceProvider.GetHashCode());
                    var store = scope.ServiceProvider.GetRequiredService<IObjectStore>();
                    var next = await store.GetNextAsync();
                    logger.LogInformation("{next}", next);
                    var processor = scope.ServiceProvider.GetRequiredService<IObjectProcessor>();
                    await processor.ProcessAsync(next);
                    logger.LogInformation("Processing {name}.", next.Name);
                    var relay = scope.ServiceProvider.GetRequiredService<IObjectRelay>();
                    await relay.RelayAsync(next);
                    logger.LogInformation("Processed results have been relayed.");
                    var marked = await store.MarkAsync(next);
                    logger.LogInformation("Marked as processed: {next}", marked);
                }
                finally
                {
                    logger.LogInformation(
                        "Finished scoped work, provider hash: {hash}.{nl}",
                        scope.ServiceProvider.GetHashCode(), Environment.NewLine);
                }
            }
        }
    }
}
前のコードでは、アプリが実行されている間、バックグラウンド サービスは次のようになります。
- IServiceScopeFactory に依存する。
 - 他のサービスを解決するための IServiceScope を作成します。
 - 利用に向けてスコープ付のサービスを解決する。
 - オブジェクトを処理してから、それらを中継し、最後に処理済みとしてマークする。
 
サンプル ソース コードから、スコープ付きサービスの有効期間が IHostedService の実装にどのような恩恵をもたらすかを確認できます。
キー付きサービス
.NET 8 以降では、キーに基づくサービスの登録と参照がサポートされています。つまり、複数のサービスを別のキーに登録し、このキーを参照に使用できます。
たとえば、インターフェイス IMessageWriter の異なる実装 (MemoryMessageWriter と QueueMessageWriter) がある場合を考えましょう。
これらのサービスは、次のようにパラメーターとしてキーをサポートするサービス登録メソッド (前述) のオーバーロードを使用して登録できます。
services.AddKeyedSingleton<IMessageWriter, MemoryMessageWriter>("memory");
services.AddKeyedSingleton<IMessageWriter, QueueMessageWriter>("queue");
              keyはstringに限定されません。 
              keyは、型がobjectを正しく実装している限り、任意のEqualsにすることができます。
              IMessageWriter を使用するクラスのコンストラクターで、次のように FromKeyedServicesAttribute を追加して解決するサービスのキーを指定します。
public class ExampleService
{
    public ExampleService(
        [FromKeyedServices("queue")] IMessageWriter writer)
    {
        // Omitted for brevity...
    }
}
関連項目
.NET