DIコンテナの使い方
別記事にて依存関係逆転の原則とDI(Dependency Injection)の手法について記載いたしました。今回はこの手法を使うときに便利なDIコンテナの使い方を紹介します。
前提条件
以下のツールを事前にインストールしておいてください
- Visual Studio また以下のライブラリをNugetパッケージよりインストールしてください
- Microsoft.Extensions.DependencyInjection
動作環境
- Visual Studio Community 2022
- .NET6.0
早速DIコンテナを使ってみる
まず使用するインターフェースとクラスを用意します。
public interface ILogger
{
void Info(string message);
}
public class Logger : ILogger
{
public void Info(string message)
{
Console.WriteLine(message)
}
}
次にDIコンテナを作成します。
DIコンテナは「ServiceCollection」クラスを使用します。
この「ServiceCollection」クラスはDIの情報を格納するクラスで、ここにインターフェースとクラスを登録していきます。
※TestFuncClassはこの後、作成します。
using Microsoft.Extensions.DependencyInjection;
・・・
public class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddSingleton<ILogger, Logger>();
TestFuncClass inst = new TestFuncClass();
inst.Execute(services);
}
}
インターフェースとクラスの登録には3種類のメソッドが用意されており、
状況によって使い分けます。各メソッドの使い方等については後述にて記載いたします。
-
AddSingleton アプリケーション全体で一度だけインスタンスが生成され、全てのリクエストで同じインスタンスが再利用されます。
-
AddScoped 各スコープごとに新しいインスタンスが生成されます。
-
AddTransient 各リクエストごとに新しいインスタンスが生成されます。
次にこのDIコンテナを使ったクラスを作成します。
BuildServiceProvider()メソッドはServiceCollectionに登録されたサービスのインスタンスを解決し、提供する役割を持ちます。
Executeメソッド内ではBuildServiceProvider()からGetServiceメソッドを使って、ILoggerを継承したLoggerのインスタンスを受け取ります。これでTestFuncClassはILoggerを継承したLoggerクラスのメソッドを使用することができます。
public class TestFuncClass
{
public void Execute(ServiceCollection services)
{
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger>();
logger.Info(“test”);
}
}
またLogger内にstring型の引数を持つコンストラクタがある場合、インスタンスとインターフェースの登録は以下のようになります。
public class Logger : ILogger
{
private string _subMessage;
public Logger_Part2(string message)
{
_subMessage = message;
}
public void Info(string message)
{
Console.WriteLine(message + _subMessage);
}
}
public class Program
{
static void Main(string[] args)
{
TestFuncClass inst = new TestFuncClass();
var services = new ServiceCollection();
services.AddSingleton<ILogger>(provider => new Logger(“追加メッセージ”));
inst.Execute(services);
}
}
DIコンテナへの登録の3種類の確認
前述にてDIコンテナへ登録する際に3つのメソッドがあることを記載しました。
ここではその使い方や動作の違いを確認していきたいと思います。
ILoggerとLoggerクラスを以下のように修正します。
public interface ILogger
{
stirng SubMessage{ get; set;}
void Info(string message);
}
public class Logger : ILogger
{
public string SubMessage { get; set; }
public void Info(string message)
{
Console.WriteLine(message)
}
}
- AddSingleton
アプリケーション全体で一度だけインスタンスが生成されるので、インスタンスの受け取りを再度行っても、同じインスタンスが取得されlogger.SubMessageに入れた値がそのままコンソールへ表示されています。
public class Program
{
Static void Main(string[] args)
{
var services = new ServiceCollection();
// DIコンテナへの登録
services.AddSingleton<ILogger,Logger>();
var serviceProvider = services.BuildServiceProvider();
// インスタンス受け取り
var logger = serviceProvider.GetService<ILogger>();
logger.SubMessage = “追加メッセージ”;
logger.Info(“test +” + logger.SubMessage);
// もう一度インスタンスを受け取る
logger = serviceProvider.GetService<ILogger>();
logger.Info(“test +” + logger.SubMessage);
}
}
出力結果
test +追加メッセージ
test +追加メッセージ
- AddScoped
AddScopedでは各スコープごとに新しいインスタンスが生成されます。
使い方としてはCreateScope()メソッドによって、スコープを作成し、Dispose()メソッドを使うことで、スコープを破棄します。
このスコープを破棄しない限りはGetService()メソッドを実行しても、同じインスタンスが返されます。 スコープを再作 成すると、新しいインスタンスが生成されます。
public class Program
{
Static void Main(string[] args)
{
var services = new ServiceCollection();
// DIコンテナへの登録
services. AddScoped<ILogger,Logger>();
var serviceProvider = services.BuildServiceProvider();
// スコープの作成
var scope = serviceProvider.CreateScope();
// インスタンス受け取り
var logger = scope.ServiceProvider.GetService<ILogger>();
logger.SubMessage = “追加メッセージ”;
logger.Info(“test +” + logger.SubMessage);
// もう一度インスタンスを受け取る
logger = scope.ServiceProvider.GetService<ILogger>();
logger.Info(“test +” + logger.SubMessage);
// スコープの破棄
Scope.Dispose();
// スコープの再作成
scope = serviceProvider.CreateScope();
logger = scope.ServiceProvider.GetService<ILogger>();
logger.Info(“test +” + logger.SubMessage);
// スコープの破棄
Scope.Dispose();
}
}
出力結果
test +追加メッセージ
test +追加メッセージ
test +
- AddTransient
リクエストのたびに新しいインスタンスが生成されるため、GetService()の実行のたびに新しいインスタンスが生成されます。そのため2つ目の出力の際、logger.SubMessageに入れた値は空の状態となっています。
public class Program
{
Static void Main(string[] args)
{
var services = new ServiceCollection();
// DIコンテナへの登録
services. AddTransient<ILogger,Logger>();
var serviceProvider = services.BuildServiceProvider();
// インスタンス受け取り
var logger = serviceProvider.GetService<ILogger>();
logger.SubMessage = “追加メッセージ”;
logger.Info(“test +” + logger.SubMessage);
// もう一度インスタンスを受け取る
logger = serviceProvider.GetService<ILogger>();
logger.Info(“test +” + logger.SubMessage);
}
}
出力結果
test +追加メッセージ
test +
以上がDIコンテナ(Microsoft.Extensions.DependencyInjection)の使い方です。
今後もこの機能を使って、依存関係逆転の原則に沿った実装を行っていきたいと思います。