ASP.NET MVC 5 で DI する #aspnetjp
ASP.NET MVC は DI(Dependency Injection: 依存性の注入)がとても簡単です。
DI については、何かに依存するものを外からもらうことで依存せずに済むくらいに思ってください。テストがしやすくなることや、クラスが絡み合わずに済むことなどがメリットとして挙げられます。さらに、DI コンテナ(依存したものの入れ物)でオブジェクトの管理もできるので、DB 接続の管理(生成の一元化、自動破棄など)などでもメリットがあります。
プロジェクトの作成とライブラリの参照
まずは、MVC プロジェクトを作成します*1。
DI の仕組みには patterns & practices お手製の Unity を使います。NuGet で Unity で検索して Unity.Mvc をインストールしてください。
パッケージマネージャーコンソールで行う場合は下記の通りです。
PM> Install-Package Unity.Mvc
ライブラリの参照を終えたら実装開始です。
DI の実装
始めに、今回の DI の対象であるインターフェイスと実装クラスを作成します。
public interface ISampleRepository { // 枠だけ使いたいので空っぽ } public class SampleRepository : ISampleRepository, IDisposable { public SampleRepository() { Debug.WriteLine("SampleRepository: Constructor"); } public void Dispose() { Debug.WriteLine("SampleRepository: Dispose"); } }
次に、このインターフェイスに対して実装クラスが注入されるように登録します。実は、NuGet によって設定用のファイルが 2 つ追加されていて、何をすべきか TODO コメントで記述されています*2。
指示の通りにコメントアウトされていたコードを書き換えます。
// TODO: Register your types here container.RegisterType<ISampleRepository, SampleRepository>();
最後に、コントローラーのコンストラクターで、先ほど登録したインターフェイスを受け取るようにすると DI が行われます。
private readonly ISampleRepository repository; public HomeController(ISampleRepository repository) { this.repository = repository; }
DI は伝搬する
ある程度のシステム規模の場合、コントローラーと Repository の間に、処理を行う Service 層を設けると思います。その場合は、コントローラーのコンストラクターで Service 、Service のコンストラクターで Repository を、それぞれ受け取るようにします。コントローラーの生成に伴う DI は伝搬しますので、下記は思った通りに動作します。
public class HomeController : Controller { private readonly SampleService service; public HomeController(SampleService service) { this.service = service; } public ActionResult Index() { return View(); } } public class SampleService { private readonly ISampleRepository repository; public SampleService(ISampleRepository repository) { this.repository = repository; } } public interface ISampleRepository { // 枠だけ使いたいので空っぽ } public class SampleRepository : ISampleRepository, IDisposable { public SampleRepository() { Debug.WriteLine("SampleRepository: Constructor"); } public void Dispose() { Debug.WriteLine("SampleRepository: Dispose"); } }
このとき、Service に関しては型が明確なので登録は不要です。
オブジェクトのライフサイクル
DI コンテナでは、オブジェクトのライフサイクルも管理されています。型の登録時に PerRequestLifetimeManager を使うことで、管理されているオブジェクトはリクエストの終了時に破棄されるようになります。DbContext や DbConnection を扱う際にとても有効です。
まずは、もう一つの TODO コメントに従って PerRequestLifetimeManager を有効化します。
// TODO: Uncomment if you want to use PerRequestLifetimeManager Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility .RegisterModule(typeof(UnityPerRequestHttpModule));
あとは、型の登録時に PerRequestLifetimeManager を指定するだけです。
container.RegisterType<ISampleRepository, SampleRepository>(
new PerRequestLifetimeManager());
実行すると、Dispose が呼ばれていることが確認できます。
先ほどの Service のように型が明確でもライフサイクルを指定したい場合は登録が必要です。
まとめ
NuGet で Unity.Mvc をインストールして、TODO コメントに従うだけです。使わない手はないですね!