15
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#】依存性注入メソッドのライフサイクル - AddTransient, AddScoped, AddSingleton

Posted at

はじめに

C#での依存性注入(DI)では、3つの依存性注入メソッドのライフサイクル(AddTransientAddScopedAddSingleton)が使用できます。

ざっくりまとめると以下のようになっています。

ライフタイム インスタンスの生成タイミング
AddTransient 要求ごとに新しいインスタンスを生成
AddScoped リクエストごとに1つのインスタンスを生成
AddSingleton アプリケーション全体で1つのインスタンスを生成

それぞれ依存性注入するのは変わりませんが、オブジェクトの生成タイミングが違うので実際に比較します。

環境

.NET 8

お試しコード

AddTransientAddScopedAddSingletonを以下のコードで切り替えながらserveエンドポイントを叩いてみて試してみます。

Program.cs
using DILifeCycle;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// 依存性注入コンテナへのサービス登録
// AddTransient、AddScoped、AddSingleton を切り替えて試す
builder.Services.AddTransient<IService, ServiceA>();
// builder.Services.AddScoped<IService, ServiceA>();
// builder.Services.AddSingleton<IService, ServiceA>();

var app = builder.Build();

// ルーティングの設定
app.MapGet("/serve", (IService service1, [FromServices] IService service2) =>
{
    var id1 = service1.Serve();
    var id2 = service2.Serve();
    return Results.Ok(new { Message = "Service Called", id1, id2 });
});

// アプリケーションの実行
app.Run();

このコードでは、2つのサービスインスタンス(service1とservice2)を解決し、UUID(InstanceId)を返すことで、インスタンスがどのように再利用されているかを確認します。

IService.cs
public interface IService
{
    Guid Serve();
}

public class ServiceA : IService
{
    public Guid InstanceId { get; private set; }

    public ServiceA()
    {
        // インスタンスIDを設定
        InstanceId = Guid.NewGuid();
        Console.WriteLine($"ServiceA instance created with ID: {InstanceId}");
    }

    public Guid Serve()
    {
        Console.WriteLine($"ServiceA is serving. Instance ID: {InstanceId}");
        return InstanceId;
    }
}

このクラスは、インスタンスが生成されるたびに新しいUUIDが割り振られ、Serve() メソッドを呼び出すことでそのUUIDを返します。

AddTransient

AddTransientは、要求ごとに新しいインスタンスを生成します。つまり、service1 と service2 でそれぞれ別のインスタンスが生成されます。

❯ curl http://localhost:5076/serve  | jq .
{
  "message": "Service Called",
  "id1": "ae19b175-dab2-45fd-beee-1fb34656f1ba",
  "id2": "b09c9024-9772-4ebf-ba34-aed3cf216a40"
}
❯ curl http://localhost:5076/serve  | jq .
{
  "message": "Service Called",
  "id1": "defcdb91-e4d2-43f8-a1ff-73cd8679dd5d",
  "id2": "e932d7fd-071a-4eda-842b-c1c6cdd797e1"
}

常にIDが変わっているので、オブジェクトが要求ごとに生成されていることがわかります。
都度newしている感じですね。

AddScoped

AddScopedでは、リクエストごとに1つのインスタンスが生成されます。リクエスト内であれば、service1 と service2 で同じインスタンスが使われます。

❯ curl http://localhost:5076/serve  | jq .
{
  "message": "Service Called",
  "id1": "8ad88baf-f438-4d7e-8ea0-06de051e352c",
  "id2": "8ad88baf-f438-4d7e-8ea0-06de051e352c"
}
❯ curl http://localhost:5076/serve  | jq .
{
  "message": "Service Called",
  "id1": "e05ff550-4646-47fe-a487-a913b150a27c",
  "id2": "e05ff550-4646-47fe-a487-a913b150a27c"
}

リクエスト毎にIDが変わっているので、オブジェクトがリクエスト毎に生成されていることがわかります。

AddSingleton

AddSingletonは、アプリケーション全体で1つのインスタンスだけが生成されます。リクエストが異なっても同じインスタンスが再利用されます。

❯ curl http://localhost:5076/serve  | jq .
{
  "message": "Service Called",
  "id1": "42f8ecf5-7b86-46a1-b3d9-aae8e17c92e8",
  "id2": "42f8ecf5-7b86-46a1-b3d9-aae8e17c92e8"
}
❯ curl http://localhost:5076/serve  | jq .
{
  "message": "Service Called",
  "id1": "42f8ecf5-7b86-46a1-b3d9-aae8e17c92e8",
  "id2": "42f8ecf5-7b86-46a1-b3d9-aae8e17c92e8"
}

IDが常に普遍なので実行時に生成されていることがわかります。

まとめ

AddTransientは、都度新しいインスタンスが生成され、状態を持たない軽量なサービスに向いています。
一方、AddScopedはリクエストごとに同じインスタンスを使用するため、1つのリクエスト内でサービスを共有する必要がある場合に便利です。
AddSingletonは、アプリケーション全体で1つのインスタンスを再利用するため、状態を保持する必要があるサービスに適しています。

脳死でAddSingletonAddScopedを使いがちですが、AddTransientはメモリ効率化やスコープを気にせず使えるという点で利点があります(この点については別の記事で掘り下げようと思います)。
それぞれのライフサイクルを理解し、適切に使い分けることが重要です。

15
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?