ASP.NET Core での依存関係の挿入Dependency injection in ASP.NET Core

作成者: Steve SmithScott AddieLuke LathamBy Steve Smith, Scott Addie, and Luke Latham

ASP.NET Core では依存関係の挿入 (DI) ソフトウェア設計パターンがサポートされています。これは、クラスとその依存関係の間で制御の反転 (IoC) を実現するための手法です。ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.

MVC コントローラー内部における依存関係の挿入に固有の情報については、ASP.NET Core でのコントローラーへの依存関係の挿入 をご覧ください。For more information specific to dependency injection within MVC controllers, see ASP.NET Core でのコントローラーへの依存関係の挿入.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

依存関係の挿入の概要Overview of dependency injection

"依存関係" とは、他のオブジェクトが必要とする任意のオブジェクトのことです。A dependency is any object that another object requires. アプリ内の他のクラスが依存している、次の WriteMessage メソッドを備えた MyDependency クラスを調べます。Examine the following MyDependency class with a WriteMessage method that other classes in an app depend upon:

public class MyDependency
{
    public MyDependency()
    {
    }

    public Task WriteMessage(string message)
    {
        Console.WriteLine(
            $"MyDependency.WriteMessage called. Message: {message}");

        return Task.FromResult(0);
    }
}

クラスで WriteMessage メソッドを使用できるようにするために、MyDependency クラスのインスタンスを作成することができます。An instance of the MyDependency class can be created to make the WriteMessage method available to a class. MyDependency クラスは IndexModel クラスの依存関係です。The MyDependency class is a dependency of the IndexModel class:

public class IndexModel : PageModel
{
    MyDependency _dependency = new MyDependency();

    public async Task OnGetAsync()
    {
        await _dependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

このクラスは MyDependency のインスタンスを作成し、これに直接依存しています。The class creates and directly depends on the MyDependency instance. コードの依存関係 (前の例など) には問題が含まれ、次の理由から回避する必要があります。Code dependencies (such as the previous example) are problematic and should be avoided for the following reasons:

  • MyDependency を別の実装で置き換えるには、クラスを変更する必要があります。To replace MyDependency with a different implementation, the class must be modified.
  • MyDependency が依存関係を含んでいる場合、これらはクラスによって構成する必要があります。If MyDependency has dependencies, they must be configured by the class. 複数のクラスが MyDependency に依存している大規模なプロジェクトでは、構成コードがアプリ全体に分散するようになります。In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • このような実装では、単体テストを行うことが困難です。This implementation is difficult to unit test. アプリはモックまたはスタブの MyDependency クラスを使用する必要がありますが、この方法では不可能です。The app should use a mock or stub MyDependency class, which isn't possible with this approach.

依存関係の挿入は、次の方法によってこれらの問題に対応します。Dependency injection addresses these problems through:

  • 依存関係の実装を抽象化するための、インターフェイスまたは基底クラスの使用。The use of an interface or base class to abstract the dependency implementation.
  • サービス コンテナー内の依存関係の登録。Registration of the dependency in a service container. ASP.NET Core には、組み込みのサービス コンテナー IServiceProvider が用意されています。ASP.NET Core provides a built-in service container, IServiceProvider. サービスはアプリの Startup.ConfigureServices メソッドに登録されています。Services are registered in the app's Startup.ConfigureServices method.
  • サービスを使用するクラスのコンストラクターへの、サービスの "挿入"。Injection of the service into the constructor of the class where it's used. 依存関係のインスタンスの作成、およびインスタンスが不要になったときの廃棄の役割を、フレームワークが担当します。The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

サンプル アプリでは、サービスがアプリに提供するメソッドが IMyDependency インターフェイスによって定義されます。In the sample app, the IMyDependency interface defines a method that the service provides to the app:

public interface IMyDependency
{
    Task WriteMessage(string message);
}

このインターフェイスは、具象型 MyDependency によって実装されます。This interface is implemented by a concrete type, MyDependency:

public class MyDependency : IMyDependency
{
    private readonly ILogger<MyDependency> _logger;

    public MyDependency(ILogger<MyDependency> logger)
    {
        _logger = logger;
    }

    public Task WriteMessage(string message)
    {
        _logger.LogInformation(
            "MyDependency.WriteMessage called. Message: {MESSAGE}", 
            message);

        return Task.FromResult(0);
    }
}

MyDependency では、コンストラクター内で ILogger<TCategoryName> が要求されます。MyDependency requests an ILogger<TCategoryName> in its constructor. 依存関係の挿入をチェーン形式で使用することはよくあります。It's not unusual to use dependency injection in a chained fashion. 次に、要求されたそれぞれの依存関係が、それ自身の依存関係を要求します。Each requested dependency in turn requests its own dependencies. コンテナーによってグラフ内の依存関係が解決され、完全に解決されたサービスが返されます。The container resolves the dependencies in the graph and returns the fully resolved service. 解決する必要がある依存関係の集合的なセットは、通常、"依存関係ツリー"、"依存関係グラフ"、または "オブジェクト グラフ" と呼ばれます。The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

IMyDependencyILogger<TCategoryName> をサービス コンテナーに登録する必要があります。IMyDependency and ILogger<TCategoryName> must be registered in the service container. IMyDependencyStartup.ConfigureServices に登録されます。IMyDependency is registered in Startup.ConfigureServices. ILogger<TCategoryName> はログ記録の抽象化インフラストラクチャによって登録されます。したがって、これは、フレームワークによって既定で登録されるフレームワークが提供するサービスです。ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.

コンテナーでは、(ジェネリック) オープン型を活用し、すべての (ジェネリック) 構築型を登録する必要をなくすことで、ILogger<TCategoryName> を解決します。The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type:

services.AddSingleton(typeof(ILogger<T>), typeof(Logger<T>));

サンプル アプリにおいて、IMyDependency サービスは MyDependency 具象型を使用して登録されます。In the sample app, the IMyDependency service is registered with the concrete type MyDependency. 登録によって、サービスの有効期間が 1 つの要求の有効期間に範囲設定されます。The registration scopes the service lifetime to the lifetime of a single request. サービスの有効期間については、このトピックの後半で説明します。Service lifetimes are described later in this topic.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

注意

services.Add{SERVICE_NAME} 拡張メソッドは、サービスを追加 (および場合によっては構成) します。Each services.Add{SERVICE_NAME} extension method adds (and potentially configures) services. たとえば、services.AddMvc() はサービスの Razor Pages と必須の MVC を追加します。For example, services.AddMvc() adds the services Razor Pages and MVC require. アプリをこの規則に従わせることをお勧めします。We recommended that apps follow this convention. 拡張メソッドを Microsoft.Extensions.DependencyInjection 名前空間に配置して、サービス登録のグループをカプセル化します。Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

サービスのコンストラクターでビルトイン型 (string など) が必要な場合は、構成オプション パターンを使って型を挿入することができます。If the service's constructor requires a built in type, such as a string, the type can be injected by using configuration or the options pattern:

public class MyDependency : IMyDependency
{
    public MyDependency(IConfiguration config)
    {
        var myStringValue = config["MyStringKey"];

        // Use myStringValue
    }

    ...
}

クラスのコンストラクター経由でサービスのインスタンスが要求されます。サービスはプライベート フィールドに割り当てられて使用されます。An instance of the service is requested via the constructor of a class where the service is used and assigned to a private field. このフィールドは、クラス全体で必要に応じてサービスにアクセスするために使用されます。The field is used to access the service as necessary throughout the class.

サンプル アプリでは、IMyDependency インスタンスが要求され、サービスの WriteMessage メソッドを呼び出すために使用されます。In the sample app, the IMyDependency instance is requested and used to call the service's WriteMessage method:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

フレームワークが提供するサービスFramework-provided services

Startup.ConfigureServices メソッドでは、Entity Framework Core や ASP.NET Core MVC のようなプラットフォーム機能など、アプリが使うサービスを定義する必要があります。The Startup.ConfigureServices method is responsible for defining the services the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. 最初に、ConfigureServices に提供される IServiceCollection では、次のサービスが定義されています (ホストの構成方法に依存します)。Initially, the IServiceCollection provided to ConfigureServices has the following services defined (depending on how the host was configured):

サービスの種類Service Type 有効期間Lifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory 一時的Transient
Microsoft.AspNetCore.Hosting.IApplicationLifetime シングルトンSingleton
Microsoft.AspNetCore.Hosting.IHostingEnvironment シングルトンSingleton
Microsoft.AspNetCore.Hosting.IStartup シングルトンSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter 一時的Transient
Microsoft.AspNetCore.Hosting.Server.IServer シングルトンSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory 一時的Transient
Microsoft.Extensions.Logging.ILogger<TCategoryName> シングルトンSingleton
Microsoft.Extensions.Logging.ILoggerFactory シングルトンSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider シングルトンSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> 一時的Transient
Microsoft.Extensions.Options.IOptions<TOptions> シングルトンSingleton
System.Diagnostics.DiagnosticSource シングルトンSingleton
System.Diagnostics.DiagnosticListener シングルトンSingleton

サービス (および必要であればサービスが依存するサービス) を登録するためにサービス コレクションの拡張メソッドを使用できる場合は、1 つの Add{SERVICE_NAME} 拡張メソッドを使用してそのサービスが必要とするすべてのサービスを登録することが規則です。When a service collection extension method is available to register a service (and its dependent services, if required), the convention is to use a single Add{SERVICE_NAME} extension method to register all of the services required by that service. 次のコードは、拡張メソッド AddDbContext<TContext>AddIdentityCore、および AddMvc を使用して、コンテナーに追加のサービスを追加する方法の例です。The following code is an example of how to add additional services to the container using the extension methods AddDbContext<TContext>, AddIdentityCore, and AddMvc:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();
}

詳細については、API ドキュメントにある ServiceCollection クラスを参照してください。For more information, see the ServiceCollection clss in the API documentation.

サービスの有効期間Service lifetimes

登録される各サービスの適切な有効期間を選択します。Choose an appropriate lifetime for each registered service. ASP.NET Core サービスは、次の有効期間で構成できます。ASP.NET Core services can be configured with the following lifetimes:

一時的Transient

有効期間が一時的なサービス (AddTransient) は、サービス コンテナーから要求されるたびに作成されます。Transient lifetime services (AddTransient) are created each time they're requested from the service container. この有効期間は、軽量でステートレスのサービスに最適です。This lifetime works best for lightweight, stateless services.

スコープScoped

有効期間がスコープのサービス (AddScoped) は、クライアント要求 (接続) ごとに 1 回作成されます。Scoped lifetime services (AddScoped) are created once per client request (connection).

警告

ミドルウェアでスコープ サービスを使用している場合、サービスを Invoke または InvokeAsync メソッドに追加します。When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. コンストラクターを使用して挿入すると、サービスがシングルトンのように動作するよう強制されるので、コンストラクターを使用した挿入は行わないでください。Don't inject via constructor injection because it forces the service to behave like a singleton. 詳細については、ASP.NET Core のミドルウェア を参照してください。For more information, see ASP.NET Core のミドルウェア.

シングルトンSingleton

有効期間がシングルトンのサービス (AddSingleton) は、最初に要求されたときに作成されます (または、Startup.ConfigureServices が実行されて、サービス登録でインスタンスが指定された場合)。Singleton lifetime services (AddSingleton) are created the first time they're requested (or when Startup.ConfigureServices is run and an instance is specified with the service registration). 以降の要求は、すべて同じインスタンスを使用します。Every subsequent request uses the same instance. アプリをシングルトンで動作させる必要がある場合は、サービス コンテナーによるサービスの有効期間の管理を許可することをお勧めします。If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. クラス内のオブジェクトの有効期間を管理するために、シングルトンの設計パターンを実装してユーザー コードを提供しないでください。Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.

警告

シングルトンからスコープ サービスを解決するのは危険です。It's dangerous to resolve a scoped service from a singleton. 後続の要求を処理する際に、サービスが正しくない状態になる可能性があります。It may cause the service to have incorrect state when processing subsequent requests.

サービス登録メソッドService registration methods

各サービス登録拡張メソッドでは、特定のシナリオで役立つオーバーロードが提供されます。Each service registration extension method offers overloads that are useful in specific scenarios.

メソッドMethod 自動Automatic
objectobject
破棄disposal
複数Multiple
実装implementations
引数を渡すPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
例:Example:
services.AddScoped<IMyDep, MyDep>();
はいYes はいYes いいえNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
次に例を示します。Examples:
services.AddScoped<IMyDep>(sp => new MyDep());
services.AddScoped<IMyDep>(sp => new MyDep("A string!"));
はいYes はいYes はいYes
Add{LIFETIME}<{IMPLEMENTATION}>()
例:Example:
services.AddScoped<MyDep>();
はいYes ×No いいえNo
Add{LIFETIME}<{SERVICE}>(new {IMPLEMENTATION})
次に例を示します。Examples:
services.AddScoped<IMyDep>(new MyDep());
services.AddScoped<IMyDep>(new MyDep("A string!"));
いいえNo [はい]Yes はいYes
Add{LIFETIME}(new {IMPLEMENTATION})
次に例を示します。Examples:
services.AddScoped(new MyDep());
services.AddScoped(new MyDep("A string!"));
いいえNo ×No はいYes

型の廃棄の詳細については、「サービスの破棄」を参照してください。For more information on type disposal, see the Disposal of services section. 実装が複数の場合の一般的なシナリオとしては、テスト用に型のモックを作成します。A common scenario for multiple implementations is mocking types for testing.

TryAdd{LIFETIME} メソッドでは、登録された実装がまだ存在しない場合にのみサービスが登録されます。TryAdd{LIFETIME} methods only register the service if there isn't already an implementation registered.

次の例の最初の行では、IMyDependencyMyDependency を登録します。In the following example, the first line registers MyDependency for IMyDependency. 2 行目では何も行われません。IMyDependency には登録された実装が既に含まれているからです。The second line has no effect because IMyDependency already has a registered implementation:

services.AddSingleton<IMyDependency, MyDependency>();
// The following line has no effect:
services.TryAddSingleton<IMyDependency, DifferentDependency>();

詳細については次を参照してください:For more information, see:

TryAddEnumerable(ServiceDescriptor) メソッドでは、"同じ型" の実装がまだ存在しない場合にのみサービスが登録されます。TryAddEnumerable(ServiceDescriptor) methods only register the service if there isn't already an implementation of the same type. 複数のサービスは、IEnumerable<{SERVICE}> によって解決されます。Multiple services are resolved via IEnumerable<{SERVICE}>. サービスを登録するとき、開発者は同じ型のものがまだ追加されていない場合にのみインスタンスを追加します。When registering services, the developer only wants to add an instance if one of the same type hasn't already been added. 一般に、このメソッドは、インスタンスのコピーが 2 つコンテナーに登録されるのを回避するために、ライブラリ作成者によって使用されます。Generally, this method is used by library authors to avoid registering two copies of an instance in the container.

次の例の最初の行では、IMyDep1MyDep を登録します。In the following example, the first line registers MyDep for IMyDep1. 2 行目では IMyDep2MyDep を登録します。The second line registers MyDep for IMyDep2. 3 行目では何も行われません。IMyDep1 には MyDep の登録済みの実装が既に含まれているからです。The third line has no effect because IMyDep1 already has a registered implementation of MyDep:

public interface IMyDep1 {}
public interface IMyDep2 {}

public class MyDep : IMyDep1, IMyDep2 {}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep2, MyDep>());
// Two registrations of MyDep for IMyDep1 is avoided by the following line:
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());

コンストラクターの挿入の動作Constructor injection behavior

サービスは 2 つのメカニズムによって解決できます。Services can be resolved by two mechanisms:

  • IServiceProvider
  • ActivatorUtilities – 依存関係の挿入コンテナーにサービスを登録することなくオブジェクトの作成を許可します。ActivatorUtilities – Permits object creation without service registration in the dependency injection container. ActivatorUtilities はユーザー側の抽象化 (タグ ヘルパー、MVC コントローラー、モデル バインダーなど) で使用されます。ActivatorUtilities is used with user-facing abstractions, such as Tag Helpers, MVC controllers, and model binders.

コンストラクターは、依存関係の挿入によって提供されない引数を受け取ることができますが、引数は既定値を割り当てる必要があります。Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

IServiceProvider または ActivatorUtilities によってサービスを解決する場合、コンストラクターの挿入には "パブリック" コンストラクターが必要です。When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

ActivatorUtilities によってサービスを解決する場合、コンストラクターの挿入に必要なことは、該当するコンストラクターが 1 つだけ存在することです。When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. コンストラクターのオーバーロードはサポートされていますが、依存関係の挿入によってすべての引数を設定できるオーバーロードは 1 つしか存在できません。Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Entity Framework コンテキストEntity Framework contexts

Entity Framework コンテキストでは通常、範囲が指定された有効期間が利用され、サービス コンテナーに追加されます。これは、Web アプリ データベース操作は通常、その範囲がクライアント要求に設定されるためです。Entity Framework contexts are usually added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. データベース コンテキストの登録時、AddDbContext<TContext> オーバーロードによって有効期間が指定されなかった場合、既定の有効期間が範囲となります。The default lifetime is scoped if a lifetime isn't specified by an AddDbContext<TContext> overload when registering the database context. 有効期間が与えられたサービスの場合、サービスより有効期間が短いデータベース コンテキストを使用できません。Services of a given lifetime shouldn't use a database context with a shorter lifetime than the service.

有効期間と登録のオプションLifetime and registration options

有効期間と登録のオプションの違いを示すために、タスクを一意の識別子 OperationId を備えた操作として表す、次のインターフェイスについて考えます。To demonstrate the difference between the lifetime and registration options, consider the following interfaces that represent tasks as an operation with a unique identifier, OperationId. 次のインターフェイスに対して操作のサービスの有効期間がどのように構成されているかに応じて、コンテナーは、クラスに要求されたときに、サービスの同じインスタンスか別のインスタンスを提供します。Depending on how the lifetime of an operations service is configured for the following interfaces, the container provides either the same or a different instance of the service when requested by a class:

public interface IOperation
{
    Guid OperationId { get; }
}

public interface IOperationTransient : IOperation
{
}

public interface IOperationScoped : IOperation
{
}

public interface IOperationSingleton : IOperation
{
}

public interface IOperationSingletonInstance : IOperation
{
}

インターフェイスは Operation クラス内で実装されます。The interfaces are implemented in the Operation class. 指定されない場合、Operation コンストラクターは GUID を生成します。The Operation constructor generates a GUID if one isn't supplied:

public class Operation : IOperationTransient, 
    IOperationScoped, 
    IOperationSingleton, 
    IOperationSingletonInstance
{
    public Operation() : this(Guid.NewGuid())
    {
    }

    public Operation(Guid id)
    {
        OperationId = id;
    }

    public Guid OperationId { get; private set; }
}

OperationService が登録されます。これは、その他の Operation 型のそれぞれに依存します。An OperationService is registered that depends on each of the other Operation types. 依存関係の挿入を経由して OperationService が要求される場合、これは各サービスの新しいインスタンスか、依存関係のあるサービスの有効期間に基づく既存のインスタンスを受信します。When OperationService is requested via dependency injection, it receives either a new instance of each service or an existing instance based on the lifetime of the dependent service.

  • コンテナーからの要求時に一時的なサービスが作成されるとき、IOperationTransient サービスの OperationIdOperationServiceOperationId は異なります。When transient services are created when requested from the container, the OperationId of the IOperationTransient service is different than the OperationId of the OperationService. OperationServiceIOperationTransient クラスの新しいインスタンスを受け取ります。OperationService receives a new instance of the IOperationTransient class. 新しいインスタンスは異なる OperationId を生成します。The new instance yields a different OperationId.
  • クライアント要求ごとにスコープ サービスが作成されるとき、IOperationScoped サービスの OperationId は、クライアント要求内の OperationService のものと同じになります。When scoped services are created per client request, the OperationId of the IOperationScoped service is the same as that of OperationService within a client request. クライアント要求間で、両方のサービスは異なる OperationId 値を共有します。Across client requests, both services share a different OperationId value.
  • シングルトンとシングルトン インスタンスのサービスが 1 回作成され、すべてのクライアント要求とすべてのサービス間で使用されるとき、OperationId はすべてのサービス要求の間で一定です。When singleton and singleton-instance services are created once and used across all client requests and all services, the OperationId is constant across all service requests.
public class OperationService
{
    public OperationService(
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance instanceOperation)
    {
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = instanceOperation;
    }

    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }
}

Startup.ConfigureServices では、各型が名前で指定されている有効期間に従ってコンテナーに追加されます。In Startup.ConfigureServices, each type is added to the container according to its named lifetime:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

IOperationSingletonInstance サービスは、Guid.Empty の既知の ID を持った特定のインスタンスを使用しています。The IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty. この型が使用されるタイミングは明らかです (その GUID はすべてゼロです)。It's clear when this type is in use (its GUID is all zeroes).

サンプル アプリでは、個々の要求内、および個々の要求間におけるオブジェクトの有効期間が示されます。The sample app demonstrates object lifetimes within and between individual requests. サンプル アプリの IndexModel には、各種類の IOperation 型と OperationService が必要です。The sample app's IndexModel requests each kind of IOperation type and the OperationService. 次に、ページ モデル クラスとサービスのすべての OperationId 値が、プロパティの割り当てを通じてページに表示されます。The page then displays all of the page model class's and service's OperationId values through property assignments:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

2 つの要求の結果を、以下の 2 つの出力に示します。Two following output shows the results of two requests:

最初の要求:First request:

コントローラーの操作:Controller operations:

一時的: d233e165-f417-469b-a866-1cf1935d2518Transient: d233e165-f417-469b-a866-1cf1935d2518
スコープ:5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
シングルトン:01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
インスタンス:00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationService の操作:OperationService operations:

一時的: c6b049eb-1318-4e31-90f1-eb2dd849ff64Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64
スコープ:5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
シングルトン:01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
インスタンス:00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

2 番目の要求:Second request:

コントローラーの操作:Controller operations:

一時的: b63bd538-0a37-4ff1-90ba-081c5138dda0Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0
スコープ:31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
シングルトン:01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
インスタンス:00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationService の操作:OperationService operations:

一時的: c4cbacb8-36a2-436d-81c8-8c1b78808aafTransient: c4cbacb8-36a2-436d-81c8-8c1b78808aaf
スコープ:31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
シングルトン:01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
インスタンス:00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

要求内および要求間で、どの OperationId 値が変化しているかを確認してください。Observe which of the OperationId values vary within a request and between requests:

  • "一時的な" オブジェクトは常に異なります。Transient objects are always different. 1 番目と 2 番目のクライアント要求の一時的な OperationId 値は、OperationService 操作とクライアント要求間の両方に対して異なります。The transient OperationId value for both the first and second client requests are different for both OperationService operations and across client requests. 個々のサービス要求とクライアント要求に対して、新しいインスタンスが提供されます。A new instance is provided to each service request and client request.
  • Scoped オブジェクトは、1 つのクライアント要求内では同じですが、複数のクライアント要求間では異なります。Scoped objects are the same within a client request but different across client requests.
  • "シングルトン" オブジェクトは、Operation インスタンスが Startup.ConfigureServices で提供されるかどうかに関係なく、すべてのオブジェクトとすべての要求について同じです。Singleton objects are the same for every object and every request regardless of whether an Operation instance is provided in Startup.ConfigureServices.

main からサービスを呼び出すCall services from main

IServiceScopeFactory.CreateScope を使用して IServiceScope を作成し、アプリのスコープ内のスコープ サービスを解決します。Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. この方法は、起動時に初期化タスクを実行するために、スコープ サービスにアクセスするのに便利です。This approach is useful to access a scoped service at startup to run initialization tasks. 次の例では、Program.MainMyScopedService のコンテキストを取得する方法を示します。The following example shows how to obtain a context for the MyScopedService in Program.Main:

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    using (var serviceScope = host.Services.CreateScope())
    {
        var services = serviceScope.ServiceProvider;

        try
        {
            var serviceContext = services.GetRequiredService<MyScopedService>();
            // Use the context here
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred.");
        }
    }

    host.Run();
}

スコープの検証Scope validation

アプリが開発環境で実行されている場合、既定のサービス プロバイダーは次を確認するためのチェックを実行します。When the app is running in the Development environment, the default service provider performs checks to verify that:

  • スコープ サービスが、ルート サービス プロバイダーによって直接的または間接的に解決されない。Scoped services aren't directly or indirectly resolved from the root service provider.
  • スコープ サービスが、シングルトンに直接または間接に挿入されない。Scoped services aren't directly or indirectly injected into singletons.

BuildServiceProvider が呼び出されると、ルート サービス プロバイダーが作成されます。The root service provider is created when BuildServiceProvider is called. ルート サービス プロバイダーの有効期間は、プロバイダーがアプリで開始されるとアプリとサーバーの有効期間に対応し、アプリのシャットダウンには破棄されます。The root service provider's lifetime corresponds to the app/server's lifetime when the provider starts with the app and is disposed when the app shuts down.

スコープ サービスは、それを作成したコンテナーによって破棄されます。Scoped services are disposed by the container that created them. ルート コンテナーにスコープ サービスが作成されると、サービスはアプリ/サーバーのシャットダウン時に、ルート コンテナーによってのみ破棄されるため、サービスの有効期間は実質的にシングルトンに昇格されます。If a scoped service is created in the root container, the service's lifetime is effectively promoted to singleton because it's only disposed by the root container when app/server is shut down. BuildServiceProvider が呼び出されると、サービス スコープの検証がこれらの状況をキャッチします。Validating service scopes catches these situations when BuildServiceProvider is called.

詳細については、ASP.NET Core の Web ホスト を参照してください。For more information, see ASP.NET Core の Web ホスト.

要求サービスRequest Services

HttpContext からの ASP.NET Core 要求内で使用可能なサービスは、HttpContext.RequestServices コレクションを通じて公開されます。The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.

要求サービスは、アプリの一部として構成および要求されるサービスを表します。Request Services represent the services configured and requested as part of the app. オブジェクトで依存関係を指定すると、これらは ApplicationServices ではなく RequestServices で検出された型で満たされます。When the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

一般に、アプリから直接これらのプロパティを使用しないでください。Generally, the app shouldn't use these properties directly. 代わりに、クラスのコンストラクターを介してクラスに必要な型を要求し、フレームワークに依存関係を挿入させます。Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. これにより、テストしやすいクラスが生成されます。This yields classes that are easier to test.

注意

コンストラクターのパラメーターとして依存関係を要求し、RequestServices コレクションにアクセスするようにします。Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

依存関係の挿入のためのサービスの設計Design services for dependency injection

ベスト プラクティスは次のとおりです。Best practices are to:

  • 各依存関係を取得するために依存関係の挿入を使用するようサービスを設計します。Design services to use dependency injection to obtain their dependencies.
  • ステートフルな静的メソッドの呼び出しを回避します。Avoid stateful, static method calls.
  • サービス内部で依存関係のあるクラスを直接インスタンス化することを回避します。Avoid direct instantiation of dependent classes within services. 直接のインスタンス化は、コードの固有の実装につながります。Direct instantiation couples the code to a particular implementation.
  • アプリのクラスを、小さく、十分に要素に分割された、テストしやすいものにします。Make app classes small, well-factored, and easily tested.

クラスに含まれる挿入される依存関係が多すぎるように見える場合、これは通常、クラスが担当する役割が多すぎて、単一責任の原則 (SRP) に違反していることのサインです。If a class seems to have too many injected dependencies, this is generally a sign that the class has too many responsibilities and is violating the Single Responsibility Principle (SRP). 責任の一部を新しいクラスに移動することにより、クラスのリファクタリングを試みます。Attempt to refactor the class by moving some of its responsibilities into a new class. Razor Pages のページ モデル クラスと MVC コントローラー クラスは、UI の問題に集中する必要があることに留意します。Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns. ビジネス ルールとデータ アクセスの実装の詳細は、これらの個別の問題に適したクラスに分離する必要があります。Business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

サービスの破棄Disposal of services

コンテナーは、作成する IDisposable 型の Dispose を呼び出します。The container calls Dispose for the IDisposable types it creates. ユーザー コードによってコンテナーに追加されたインスタンスは、自動的に破棄されません。If an instance is added to the container by user code, it isn't disposed automatically.

// Services that implement IDisposable:
public class Service1 : IDisposable {}
public class Service2 : IDisposable {}
public class Service3 : IDisposable {}

public interface ISomeService {}
public class SomeServiceImplementation : ISomeService, IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    // The container creates the following instances and disposes them automatically:
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    services.AddSingleton<ISomeService>(sp => new SomeServiceImplementation());

    // The container doesn't create the following instances, so it doesn't dispose of
    // the instances automatically:
    services.AddSingleton<Service3>(new Service3());
    services.AddSingleton(new Service3());
}

既定のサービス コンテナーの置換Default service container replacement

組み込みのサービス コンテナーは、フレームワークと、ほとんどのコンシューマー アプリのニーズに対応することを意図したものです。The built-in service container is meant to serve the needs of the framework and most consumer apps. それがサポートしない特定の機能が必要な場合は、組み込みのコンテナーを使用することをお勧めします。We recommend using the built-in container unless you need a specific feature that it doesn't support. 組み込みのコンテナーにない、サードパーティのコンテナーでサポートされている一部の機能は次のとおりです。Some of the features supported in 3rd party containers not found in the built-in container:

  • プロパティの挿入Property injection
  • 名前に基づく挿入Injection based on name
  • 子コンテナーChild containers
  • 有効期間のカスタム管理Custom lifetime management
  • 遅延初期化に対する Func<T> のサポートFunc<T> support for lazy initialization

アダプターをサポートするいくつかのコンテナーの一覧については、「Dependency Injection readme.md file」 (Dependency Injection readme.md ファイル) を参照してください。See the Dependency Injection readme.md file for a list of some of the containers that support adapters.

次のサンプルでは、Autofac で組み込みのコンテナーが置き換えられています。The following sample replaces the built-in container with Autofac:

  • 適切なコンテナー パッケージをインストールします。Install the appropriate container package(s):

  • Startup.ConfigureServices でコンテナーを構成して、IServiceProvider を返します。Configure the container in Startup.ConfigureServices and return an IServiceProvider:

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        // Add other framework services
    
        // Add Autofac
        var containerBuilder = new ContainerBuilder();
        containerBuilder.RegisterModule<DefaultModule>();
        containerBuilder.Populate(services);
        var container = containerBuilder.Build();
        return new AutofacServiceProvider(container);
    }
    

    サード パーティのコンテナーを使用するには、Startup.ConfigureServicesIServiceProvider が返される必要があります。To use a 3rd party container, Startup.ConfigureServices must return IServiceProvider.

  • DefaultModule で Autofac を構成します。Configure Autofac in DefaultModule:

    public class DefaultModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
        }
    }
    

実行時には、Autofac を使って、型の解決と依存関係の挿入が行われます。At runtime, Autofac is used to resolve types and inject dependencies. ASP.NET Core での Autofac の使用について詳しくは、Autofac のドキュメントをご覧ください。To learn more about using Autofac with ASP.NET Core, see the Autofac documentation.

スレッド セーフThread safety

スレッド セーフのシングルトン サービスを作成します。Create thread-safe singleton services. シングルトン サービスに一時的サービスへの依存関係がある場合、シングルトンによる一時的サービスの使い方によっては、一時的サービスもスレッド セーフであることが必要な場合があります。If a singleton service has a dependency on a transient service, the transient service may also require thread safety depending how it's used by the singleton.

1 つのサービスのファクトリ メソッド (例: AddSingleton<TService(IServiceCollection, Func<IServiceProvider,TService) に対する 2 番目の引数) をスレッド セーフにする必要はありません。The factory method of single service, such as the second argument to AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), doesn't need to be thread-safe. 型 (static) のコンストラクターのように、1 つのスレッドによって 1 回呼び出されることが保証されます。Like a type (static) constructor, it's guaranteed to be called once by a single thread.

推奨事項Recommendations

  • async/await および Task ベースのサービスの解決はサポートされていません。async/await and Task based service resolution is not supported. C# では非同期コンストラクターがサポートされていないため、推奨パターンは、サービスを同期的に解決した後に、非同期メソッドを使用することです。C# does not support asynchronous constructors; therefore, the recommended pattern is to use asynchronous methods after synchronously resolving the service.

  • データと構成をサービス コンテナーに直接格納しないようにします。Avoid storing data and configuration directly in the service container. たとえば、通常、ユーザーのショッピング カートはサービス コンテナーに追加しません。For example, a user's shopping cart shouldn't typically be added to the service container. 構成では、オプション パターンを使う必要があります。Configuration should use the options pattern. 同様に、他のオブジェクトへのアクセスを許可するためだけに存在する "データ ホルダー" オブジェクトは避ける必要があります。Similarly, avoid "data holder" objects that only exist to allow access to some other object. 実際のアイテムを DI 経由で要求することをお勧めします。It's better to request the actual item via DI.

  • サービスへの静的なアクセスを回避します (たとえば、他所で使用するための IApplicationBuilder.ApplicationServices の静的な型指定)。Avoid static access to services (for example, statically-typing IApplicationBuilder.ApplicationServices for use elsewhere).

  • サービス ロケーター パターンの使用は避けてください。Avoid using the service locator pattern. たとえば、サービス インスタンスを取得する場合、DI を使用できるときに、GetService を呼び出さないでください。For example, don't invoke GetService to obtain a service instance when you can use DI instead:

    正しくない:Incorrect:

    public void MyMethod()
    {
        var options = 
            _services.GetService<IOptionsMonitor<MyOptions>>();
        var option = options.CurrentValue.Option;
    
        ...
    }
    

    正しい:Correct:

    private readonly MyOptions _options;
    
    public MyClass(IOptionsMonitor<MyOptions> options)
    {
        _options = options.CurrentValue;
    }
    
    public void MyMethod()
    {
        var option = _options.Option;
    
        ...
    }
    
  • 回避すべき別のサービス ロケーターのバリエーションは、実行時に依存関係を解決するファクトリを挿入することです。Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. この両方のプラクティスによって、複数の制御の反転方式が組み合わせられます。Both of these practices mix Inversion of Control strategies.

  • HttpContext への静的なアクセスを回避します (たとえば IHttpContextAccessor.HttpContext)。Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

どのような推奨事項であっても、それを無視する必要がある状況が発生する可能性があります。Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. 例外はまれです—ほとんどがフレームワーク自体の内の特殊なケースです。Exceptions are rare—mostly special cases within the framework itself.

DI は静的/グローバル オブジェクト アクセス パターンの代替機能です。DI is an alternative to static/global object access patterns. 静的オブジェクト アクセスと併用した場合、DI のメリットを実現することはできません。You may not be able to realize the benefits of DI if you mix it with static object access.

その他の技術情報Additional resources