Alva

Alva

programmer
github
telegram
email

dotnetの依存性注入-04.ライフサイクル

本文示例代码,均采用 .NET 6,具体的代码可以在这个仓库 Articles.DI 中获取。

前面的記事では、サービスの登録時に、services.AddSingleton<TService, TImplementation>()の形式でサービスを登録することが一貫して使用されています。このメソッドの具体的な意味は何ですか?他の類似のメソッドはありますか?また、私たちには疑問があります。コンテナがサービスを構築する際のサービスのライフサイクルはどのようなものですか?サービスは 1 回申請されるたびに 1 回構築されますか?それともプログラム全体のライフサイクルで 1 回だけ構築されますか?各サービスに異なるライフサイクルをカスタマイズする場合、どのようにすればよいですか?以下では、サービスのライフサイクルについて一緒に探求していきます。

ライフサイクル#

コンテナに登録されるサービスのライフサイクルは、次の 3 つに分類されます。

ライフサイクル作成タイミング呼び出しメソッド
瞬時 / Transient各リクエストごとに新しいインスタンスが作成されますAddTransient()
スコープ / Scoped指定されたスコープ内で最初のリクエスト時にインスタンスが作成されます
同じスコープ内での繰り返しのリクエストでは、同じインスタンスが返されます
AddScoped()
シングルトン / Singletonプログラム全体のライフサイクルで 1 回だけ作成されます
その後のすべてのリクエストでは、同じインスタンスが返されます
AddSingleton()

実際のコードを参考にすると、依存性注入の 3 つのライフサイクルをより直感的に理解することができます。以下のコードは、Microsoft の.NET ドキュメントの対応する記事で見つけることができます - チュートリアル: .NET での依存関係の注入の使用。または、このリポジトリで直接ソースコードを確認することもできます。

// https://github.com/alva-lin/Articles.DI/tree/master/ConsoleApp1
// 01. インターフェースと実装クラスを宣言する
public interface IOperation
{
    string OperationId { get; }
}

public interface ITransientOperation : IOperation {}

public interface IScopedOperation : IOperation {}

public interface ISingletonOperation : IOperation {}

public class DefaultOperation
    : ITransientOperation, IScopedOperation, ISingletonOperation
{
    // Guidを作成し、最後の4文字を取得する
    public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
// 02. 上記のいくつかの Operationに依存するサービスを作成する
public class OperationLogger
{
    private readonly ITransientOperation _transientOperation;
    private readonly IScopedOperation    _scopedOperation;
    private readonly ISingletonOperation _singletonOperation;

    public OperationLogger(ITransientOperation transientOperation,
                           IScopedOperation    scopedOperation,
                           ISingletonOperation singletonOperation)
    {
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void LogOperations(string scope)
    {
        LogOperation(_transientOperation, scope, "Always different");
        LogOperation(_scopedOperation, scope, "Changes only with scope");
        LogOperation(_singletonOperation, scope, "Always the same");
    }

    private static void LogOperation<T>(T operation, string scope, string message)
        where T : IOperation
    {
        Console.WriteLine($"{scope}: {typeof(T).Name,-19} [{operation.OperationId} {message,-23}]");
    }
}
// 03. サービスを登録し、プログラムを起動する
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateDefaultBuilder(args)
   .ConfigureServices(services =>
    {
        // 3つのOperationをそれぞれのライフサイクルに登録する
        services.AddTransient<OperationLogger>()
           .AddTransient<ITransientOperation, DefaultOperation>()
           .AddScoped<IScopedOperation, DefaultOperation>()
           .AddSingleton<ISingletonOperation, DefaultOperation>();
    })
   .Build();

ExemplifyScoping(host.Services, "Scope 1");
ExemplifyScoping(host.Services, "Scope 2");

await host.RunAsync();


static void ExemplifyScoping(IServiceProvider services, string scope)
{
    using IServiceScope serviceScope = services.CreateScope();
    IServiceProvider    provider     = serviceScope.ServiceProvider;

    var logger = provider.GetRequiredService<OperationLogger>();
    logger.LogOperations($"{scope}: Call 1 ...");
    Console.WriteLine();

    logger = provider.GetRequiredService<OperationLogger>();
    logger.LogOperations($"{scope}: Call 2 ...");
    Console.WriteLine();
}

上記のコードを実行すると、次の結果が得られます。

Scope 1: Call 1 ...: ITransientOperation [b672 Always different       ]
Scope 1: Call 1 ...: IScopedOperation    [afd8 Changes only with scope]
Scope 1: Call 1 ...: ISingletonOperation [21b3 Always the same        ]

Scope 1: Call 2 ...: ITransientOperation [b6fc Always different       ]
Scope 1: Call 2 ...: IScopedOperation    [afd8 Changes only with scope]
Scope 1: Call 2 ...: ISingletonOperation [21b3 Always the same        ]

Scope 2: Call 1 ...: ITransientOperation [ef31 Always different       ]
Scope 2: Call 1 ...: IScopedOperation    [46d1 Changes only with scope]
Scope 2: Call 1 ...: ISingletonOperation [21b3 Always the same        ]

Scope 2: Call 2 ...: ITransientOperation [9864 Always different       ]
Scope 2: Call 2 ...: IScopedOperation    [46d1 Changes only with scope]
Scope 2: Call 2 ...: ISingletonOperation [21b3 Always the same        ]

プログラムの出力ログを比較すると、各ITransientOperationの出力が異なることがわかります。一方、IScopedOperationは同じスコープ内では同じ内容を出力し、異なるスコープ間では異なる内容を出力します。最後のISingletonOperationは、常に同じ内容を出力します。これらの出力結果は、前述の表の内容とも一致しています。

参考リンク#

.NET での依存関係の注入

チュートリアル: .NET での依存関係の注入の使用

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。