UnitTestでDependencyInjectionを利用するときの部品どり

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

以前の記事で、DIするときの勉強をして、ごちゃごちゃいろいろ書いたので、ササっとコピペして使えるように部品どりコードを置いておきたい。

前提

  • Windows10 Home 21H1 19043.1706
  • VisualStudio2022 Community 17.1.4
  • WinUI3.0
  • Windows App SDK 1.0
  • NUnit単体テスト実施
  • 2022年6月の時点の調査

コード

AddTransientの場合(取るたびに異なるインスタンスが取れるパターン)

///////////////////////// 登録部分(プログラムの最初で行う) /////////////////////////

var services = new ServiceCollection();

services.AddTransient<IGetCurrentTimeModel, GetCurrentTimeModel>();              // ①
services.AddTransient<IGetCurrentTimeModel>((a) => new GetCurrentTimeModel(99)); // ②

var provider = services.BuildServiceProvider();
Ioc.Default.ConfigureServices(provider);


///////////////////////// 使う部分(登録したインスタンスを使う個所で行う) /////////////////////////

var a1 = Ioc.Default.GetRequiredService<IGetCurrentTimeModel>();    // ②が取れるのは同じだが、
var a2 = Ioc.Default.GetRequiredService<IGetCurrentTimeModel>();    // 別インスタンスが取れる

Debug.WriteLine(a1 == a2);                                          // →false(別インスタンスが取れた)

AddSingletonの場合(取るたびに同じインスタンスが取れるパターン)

///////////////////////// 登録部分(プログラムの最初で行う) /////////////////////////

// ServiceCollectionを作成
var services = new ServiceCollection();

// インスタンスを登録
services.AddSingleton<IGetCurrentTimeModel, GetCurrentTimeModel>();              // ①
services.AddSingleton<IGetCurrentTimeModel>((a) => new GetCurrentTimeModel(99)); // ②

// サービスプロバイダ、サービスを作成
var provider = services.BuildServiceProvider();
Ioc.Default.ConfigureServices(provider);


///////////////////////// 使う部分(登録したインスタンスを使う個所で行う) /////////////////////////

// 登録したインスタンスを使う
var a1 = Ioc.Default.GetRequiredService<IGetCurrentTimeModel>();    // 同じクラスを重複して登録した場合は、
var a2 = Ioc.Default.GetRequiredService<IGetCurrentTimeModel>();    // どちらも、②(引数アリのほう)の同じインスタンスが取れる

Debug.WriteLine(a1==a2);                                            // →true(同じインスタンスが取れた)

AddScopedの場合(指定したスコープ内では同じインスタンス、異なるスコープでは異なるインスタンスが取れるパターン)

/////////////////////////////////
var services = new ServiceCollection();

services.AddScoped<IGetCurrentTimeModel>((a) => new GetCurrentTimeModel(99)); // ②
services.AddScoped<IGetCurrentTimeModel, GetCurrentTimeModel>();              // ①

var provider = services.BuildServiceProvider();
provider.CreateScope();

Ioc.Default.ConfigureServices(provider);


/////////////////////////////////
IGetCurrentTimeModel gctm1;
IGetCurrentTimeModel gctm2;
IGetCurrentTimeModel gctm3;
IGetCurrentTimeModel gctm4;
using (var scope1 = Ioc.Default.CreateScope())
{
    gctm1 = scope1.ServiceProvider.GetService<IGetCurrentTimeModel>();
    gctm2 = scope1.ServiceProvider.GetService<IGetCurrentTimeModel>();
}
using (var scope2 = Ioc.Default.CreateScope())
{
    gctm3 = scope2.ServiceProvider.GetService<IGetCurrentTimeModel>();
    gctm4 = scope2.ServiceProvider.GetService<IGetCurrentTimeModel>();
}
Debug.WriteLine(gctm1 == gctm2);// スコープが同じなので、1と2は同じ、3と4も同じインスタンス。
Debug.WriteLine(gctm1 == gctm3);// スコープが異なるので、1と3は異なるインスタンス。

AddSingletonとAddTransientで同じinterfaceを登録した場合のメモ)

後勝ちになるっぽい。

/////////////////////////////////
var services = new ServiceCollection();

services.AddSingleton<IGetCurrentTimeModel, GetCurrentTimeModel>();              // ①
services.AddTransient<IGetCurrentTimeModel>((a) => new GetCurrentTimeModel(99)); // ②

var provider = services.BuildServiceProvider();
Ioc.Default.ConfigureServices(provider);


/////////////////////////////////
var a1 = Ioc.Default.GetRequiredService<IGetCurrentTimeModel>();    // 後で登録したほうの方式(SingletonかTransientか)で、
var a2 = Ioc.Default.GetRequiredService<IGetCurrentTimeModel>();    // 取れる

Debug.WriteLine(a1 == a2);                                          // →Transientが後なのでfalse(Singletonならtrueになる)

参考

以前のDIについての記事

https://tera1707.com/entry/2022/05/16/230008