こんにちは、サイオステクノロジー技術部 武井です。今回は、APS.NET Coreの主要な機能の一つであるDelendency Injection(以降、DIと呼称)について、書きたいと思います。
DIはビジネスロジックの中ではインターフェースだけを定義し、その実装を外部から注入(Injection)するという実装方法で、レトロなところでいいますと、JavaのフレームワークであるSeasar、Google謹製のフレームワークGoogle Guice、最近ではSpring Bootなどで使われており、古くから使われている方式です。
APS.NET CoreではDIが標準装備になっています。いいですね。
やってみる
ということで、以下の要件を満たすアプリケーションをDIを使って、作成します。
MVCなアプリケーションで、朝の挨拶、夜の挨拶を返すクラスをDIして、ブラウザに「Good Moring!!」「Good Evening!!」と表示される。
DIされるインターフェースとその実装クラス
DIされるインターフェースとその実装クラスを用意します。
public interface IGreeting { string Greet(); } public class GoodMorning : IGreeting { public string Greet() { return "Good Morning!!"; } } public class GoodEvening : IGreeting { public string Greet() { return "Good Evening!!"; } }上記のソースコードの詳細を解説していきます。
public interface IGreeting { string Greet(); }挨拶を返すクラスのインターフェースを定義します。Greetというメソッドを一つ持ち、その戻り値はstringです。「Good Moring!!」や「Good Evening!!」を返す想定です。
public class GoodMorning : IGreeting { public string Greet() { return "Good Morning!!"; } }IGreetingインターフェースの実装クラスです。「Good Morning!!」のstringを返します。
public class GoodEvening : IGreeting { public string Greet() { return "Good Evening!!"; } }IGreetingインターフェースの実装クラスです。「Good Evening!!」のstringを返します。
DIの設定を行うクラス
DIの設定を行うクラスを用意します。
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IGreeting,GoodMorning>(); services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } }上記のソースコードの詳細を解説していきます。
public void ConfigureServices(IServiceCollection services) { services.AddTransient<IGreeting,GoodMorning>(); services.AddMvc(); }ConfigureServiceメソッドは、ASP.NET Coreから呼び出されるメソッドで、DIの設定の中核を担うメソッドです。IServiceCollection型の引数であるserviceにAddTransientすることでDIすることが出来ます。書式は以下のとおりです。
AddTransient<[インターフェースの型],[インターフェースの実装クラスの型]>();上記のソースコードから概ね想像がつくと思いますが、[インターフェースの型]にはDIするクラスのインターフェース、[インターフェースの実装クラス]にはその名の通り、インターフェースを実装したクラスの型を指定します。ここではそれぞれ、IGreeting、GoodMorningです。AddTransientの他にもAddScoped、AddSingletonメソッドがあります。
AddTransient | DIされるたびにインスタンスを生成します。 |
AddScoped | 一度DIされると、一つのHTTPリクエスト内であれば、最初にDIされた値を使いまわします。 |
AddSingleton | 一度DIされると、アプリケーションが終了するまで、最初にDIされた値を使いまわします。 |
services.AddMvc()はAPS.NET CoreでMVCなWebアプリケーションを実現するための、IServiceCollectionの拡張メソッドなのですが、ここでは本筋ではないため、説明は割愛致します。MVCを実現するためのおまじない程度なものと思ってください。DIを説明するためにはどうしてもMVCを例に上げるのがいいもので、、、(´・ω・`)
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }上記のソースは、概ねこちらの記事で紹介したものと同じです。ちょっと違うのは、UseMVCの下りです。こちらもAPS.NET CoreでMVCなアプリケーションを実現するために必要なものですが、ここではまたしても詳細は割愛したいと思います(´・ω・`)ざっくりいいますと、ロケーション(URLのホスト名をより後の部分)の命名規則を決めるための記述になります。
public class HomeController : Controller { private IGreeting _greeting; public HomeController(IGreeting greeting) { this._greeting = greeting; } public IActionResult Index() { this.ViewBag.Greet = this._greeting.Greet(); return View(); } }MVCのCの部分、つまりControllerの部分ですね。ここでもASP.NET Coreの独特な記述もあるのですが、その役割は一般的なMVCのC(Controller)と変わりありませんので、説明は割愛致します(´・ω・`)
そして最も大事なところなのですが、先程DIしたインスタンスはここで取り出します。DIされたインスタンスを取り出す方法は、コンストラクタインジェクション、プロパティインジェクションなど色々あのですが、ASP.NET Coreでサポートしているのはコンストラクタインジェクションのみです。
上記のクラスのコンストラクタ、つまりHomeControllerの引数にIGreeting、つまりDIしたインスタンスのインターフェースを指定しています。実はこれで簡単に取り出せるのです。このgreeting変数には、StartupクラスのConfigureServiceメソッドの中のAddTransientメソッドのジェネリックで指定したGoodMorningクラスをNewしたインスタンスが入っていいます。
そしてIndexメソッドでは、View(後にご説明するIndex.cshtml)に渡すために、Greetメソッドを実行して、挨拶の文字列を取り出しています。
Viewのソース(Index.cshtml)は以下のとおりです。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> @this.ViewBag.Greet </body> </html>もちろんこのWebアプリケーションの結果は以下のとおりです。
Good Morning!!先程のStartupクラスのConfigureServicesを以下のように書き換えてみます。
public void ConfigureServices(IServiceCollection services) { services.AddTransient<IGreeting,GoodEvening>(); services.AddMvc(); }Webアプリケーションの実行結果は以下のようになります。
Good Evening!!HomeContorllerクラスの実装は一切変えず、実行結果を返ることが出来ました。いいですね、DI。
DIのスコープ
ちょっとDIのスコープを変えてみる実験をしてみます。先程、DIのスコープには3つあり、それぞれAddTransient、AddScope、AddSingletonでスコープを変えることができると説明しました。スコープがTransientとSingletonの場合の挙動の変化を確かめてみましょう。
Transientの場合
では、先程説明したGoodMorningクラスのConfugureServicesメソッドを以下のように変えてみます。
public class GoodMorning : IGreeting { private int i = 0; public string Greet() { i++; return i + "回目のGood Morning!!"; } }Webアプリケーションを実行しますと、以下のようになります。何回ブラウザをリロードしても以下のメッセージは変わりません。Transientなので、DIコンテナから取り出すたびにインスタンスを生成し直すので、いつまでも挨拶は1回しかしないのです。
1回目のGood Morning!!Singletonの場合
次に、StartupクラスのConfigureServicesメソッドを以下のように変更してみます。
public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IGreeting,GoodEvening>(); services.AddMvc(); }AddTransientをAddSingletonに変えてみました。
すると、ブラウザをリロードするたびに挨拶の回数もどんどん増えていきます。これは、Singletonにしたので、DIコンテナから取り出すときは、アプリケーションが起動してから一番最初に生成したインスタンスを使い回すからです。
3回目のGood Morning!!すごいですね!!
まとめ
DIで育ってきた私としましては、ASP.NET CoreがDIを標準装備しているのは嬉しい限りです。どんどん使いましょう、DI。No DI,No Life!!