ASP.NET Core でよく使われる(?) Microsoft.Extensions.DependencyInjection をクラスライブラリやコンソールアプリケーションで使用したときの挙動についての備忘録です。
Microsoft.Extensions.DependencyInjection の DI コンテナの依存性の注入方法
主に以下の3パターンがあります。
AddTransient
AddScoped
AddSingleton
AddSingleton
はその名前の通りなのでいいのですが、残り2つがぱっと見わからないので、それを今回確認しました。
わかったこと
どうやら、AddTransient
と AddScoped
はスコープ設定をするかどうかで挙動がかわるみたいです。
実際に以下のように試してみました。
試したこと
まず、適当にDTOを用意します。
interface ISingleton
{
string Hoge { get; set; }
}
class Singleton : ISingleton
{
public Singleton()
{
Hoge = Guid.NewGuid().ToString();
}
public string Hoge { get; set; }
}
interface IScoped
{
string Piyo { get; set; }
}
class Scoped : IScoped
{
public Scoped()
{
Piyo = Guid.NewGuid().ToString();
}
public string Piyo { get; set; }
}
interface ITransient
{
string Fuga { get; set; }
}
class Transient : ITransient
{
public Transient()
{
Fuga = Guid.NewGuid().ToString();
}
public string Fuga { get; set; }
}
テスト用のソースとして以下を用意しました。
public void GetServiceTest()
{
var services = new ServiceCollection();
// 依存性の注入
services.AddSingleton<ISingleton, Singleton>();
services.AddScoped<IScoped, Scoped>();
services.AddTransient<ITransient, Transient>();
// DIコンテナの生成
var serviceProvider = services.BuildServiceProvider();
Console.WriteLine("スコープなし1回目: Singleton:" + serviceProvider.GetRequiredService<ISingleton>().Hoge);
Console.WriteLine("スコープなし1回目: Scoped:" + serviceProvider.GetRequiredService<IScoped>().Piyo);
Console.WriteLine("スコープなし1回目: Transient:" + serviceProvider.GetRequiredService<ITransient>().Fuga);
Console.WriteLine("スコープなし2回目: Singleton:" + serviceProvider.GetRequiredService<ISingleton>().Hoge);
Console.WriteLine("スコープなし2回目: Scoped:" + serviceProvider.GetRequiredService<IScoped>().Piyo);
Console.WriteLine("スコープなし2回目: Transient:" + serviceProvider.GetRequiredService<ITransient>().Fuga);
using (var scope = serviceProvider.CreateScope())
{
Console.WriteLine("スコープあり: Singleton:" + scope.ServiceProvider.GetRequiredService<ISingleton>().Hoge);
Console.WriteLine("スコープあり: Scoped:" + scope.ServiceProvider.GetRequiredService<IScoped>().Piyo);
Console.WriteLine("スコープあり: Transient:" + scope.ServiceProvider.GetRequiredService<ITransient>().Fuga);
}
}
これを実行した結果です。
スコープなし1回目: Singleton:3a3fdfd6-50d7-4c9b-9dcf-9a316a554b06
スコープなし1回目: Scoped:f6c3fe39-b608-4083-aff5-98dc6b66b8de
スコープなし1回目: Transient:b209bf1d-c83f-434a-8957-9bc528e9405c
スコープなし2回目: Singleton:3a3fdfd6-50d7-4c9b-9dcf-9a316a554b06
スコープなし2回目: Scoped:f6c3fe39-b608-4083-aff5-98dc6b66b8de
スコープなし2回目: Transient:84d37074-17dc-4f93-845b-4e7125361955
スコープあり: Singleton:3a3fdfd6-50d7-4c9b-9dcf-9a316a554b06
スコープあり: Scoped:990c5065-586c-4e11-b8e7-094a59c1b511
スコープあり: Transient:e10ebd8c-2f23-42c2-ba0d-7d1b2e2cb7f5
まとめ
時間がないのできなりまとめですが、以下の通りです。
ASP.NET Core では、デフォルトで以下のように動作します。
依存性の注入方法 | GetSerivece() を実行したときの振る舞い |
---|---|
AddTransient |
同一リクエストであっても、常に新しいインスタンスが生成される |
AddScoped |
同一のリクエストであれば、同じインスタンスが返ってくる。ただし、異なるリクエストならば新しいインスタンスが生成される |
AddSingleton |
異なるリクエストであっても、常に同じンスタンスが返ってくる |
コンソールアプリやクラスライブラリでは以下のように動作します。
依存性の注入方法 | GetSerivece() を実行したときの振る舞い |
---|---|
AddTransient |
常に新しいインスタンスが生成される |
AddScoped |
スコープを自身で設定する必要がある。何もスコープを設定しないときは、グローバルスコープとなり常に同じインスタンスが返ってくる |
AddSingleton |
常に同じンスタンスが返ってくる |
スコープを設定するために IServiceScopeFactory
なるものがあるらしく、自前でそれを使用することでスコープの制御ができるみたいです。
たぶんですけど、ASP.NET Core ではそのスコープ制御を隠蔽して勝手にやってくれているのだと思います。
コメント