何回シリーズになるか分かりませんが、「プログラミング ASP.NET MVC」を読んでASP.NET MVCのクラス構成について考えた記事を書いています。これまで2回書いており、内容は以下の通りです。
第3回目はpatterns&practicesのProject Silkで利用されているUnityを利用したDIについて書きます。Unityは制御の反転(IoC:Inversion of Control)を実現するためのフレームワークです。IoCとはDIの1つ上、抽象化された概念だと思ってください。IoCを実現するための実装の1つがDIになります。(用語や概念を厳密に説明できるほど理解が進んでいないです)詳しいことを知りたい方は、以下のページを良く読んでください。
制御の反転
Unity におけるポリシーの挿入
Project SilkやUnityについてはすでにブログで書いている方がいらっしゃるので、最大限参考にさせていていただきました。
ASP.NET MVC 3 と Unity の組み合わせを試し中 (shibayanさん)
Project SilkにならってUnityを使ってみる (miso_soup3さん)
今回は(2)で作成したソースをUnityに対応させたいと思います。まずはNugetでインストールします。
PM> Install-Package Unity
依存関係 ‘CommonServiceLocator (≥ 1.0)’ の解決を試みています。
‘CommonServiceLocator 1.0’ が正常にインストールされました。
Unity を Microsoft patterns & practices からダウンロードします。このライセンス条項は http://www.opensource.org/licenses/ms-pl で参照できます。固有のライセンス条項がある追加の依存関係がパッケージに含まれていないかどうかを確認してください。パッケージおよび依存関係を使用することで、該当するライセンス条項に同意したものとみなされます。ライセンス条項に同意しない場合は、関係のあるコンポーネントをデバイスから削除してください。
‘Unity 2.1.505.0’ が正常にインストールされました。
‘CommonServiceLocator 1.0’ が MvcHogeApp に正常に追加されました。
‘Unity 2.1.505.0’ が MvcHogeApp に正常に追加されました。
Web.configにインターフェースと実装クラスのマッピング設定を記述します。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<namespace name="MvcHogeApp" />
<namespace name="MvcHogeApp.WorkerService" />
<namespace name="MvcHogeApp.Models" />
<assembly name="MvcHogeApp" />
<container>
<register type="IHogeWorkerService" mapTo="HogeWorkerService" />
<register type="IHogeDao" mapTo="HogeDao" />
</container>
</unity>
</configuration>
Project SilkではWeb.configではなく専用の設定ファイル(Unity.config)にマッピング情報を記載していました。また<register>タグで指定するマッピング情報では名前空間を省略せずに書いてありました。
次にリゾルバの設定です、なぜかIDependencyResolverインターフェースを実装した、欲しいインスタンスを取得するリゾルバが提供されていないので、自分でコードを書く必要があります。今回はProjet Silkで実装されている「UnityDependencyResolver.cs」をそのまま利用しました。このとき知ったのは、Unity関連のクラスはMicrosoft.Practices.Unity名前空間にあるんですね。Unityがルートじゃないんですね。
そしてリゾルバの設定です、Global.asax.csのApplication_Startに設定を行います。
protected void Application_Start()
{
IUnityContainer container = new UnityContainer();
container.LoadConfiguration();
IDependencyResolver resolver = new UnityDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
}
こうすることで、引数なしのコンストラクタが不要になります。Web.configに記載したマッピングの設定にしたがってUnityがインスタンスを取得してくれるためです。コードはこうなります。(わかりやすくするために、以前のコードも残してあります)多少コードがすっきりしたことがわかると思います。
public class HogeWorkerService : IHogeWorkerService
{
private IHogeDao Dao;
// 通常のコンストラクタ
//public HogeWorkerService()
//{
// Dao = new HogeDao();
//}
// DI用(コンストラクタインジェクション)コンストラクタ
public HogeWorkerService(IHogeDao dao)
{
Dao = dao;
}
// コントローラーから呼び出されるメソッド
public HogeViewModels MakeHoge()
{
// 省略
}
今まで通り、引数付きのコンストラクタは単体テスト用に必要です。
Unityを使った感想ですが、それほどDIフレームワークを利用するメリットが思いつかないです。コードがややすっきりするのは確かですが、デフォルトコンストラクタがなくなるだけなので、それほど劇的に変わりません。逆に、設定ファイルに型・インターフェース名を文字列として記載するのでタイプミスでエラーが発生してハマるリスクのほうが大きい気が...(昔S2Containerのdiconファイルの記述でハマった悪夢が蘇りました)
ただ、Release/Debug用に設定ファイルを分けることで、取得するインスタンスを切り替えることができますし、単体テストプロジェクトにも設定ファイルを作成して、リゾルバを設定することで引数付きコンストラクタもなくすことができると思います。(Project Silkではそこまでやっていませんでしたが)使うのであれば、プロダクションコード、テストプロジェクトまで徹底的に使わないと、使い方を覚えるコストをペイできないんじゃないかなぁ、と思いました。手放しで「素晴らしい!」とは言えませんでした。う~ん。
Unityの単純な使い方ではなくて、シナリオがあってUnityを使うとこんなに素晴らしくなるという説明を誰かブログに書いてくれないですかねぇ。自分は書けませんでした。
今回はUnityをテーマに書きました。作成したソースはSkyDriveにアップしてあります。「MvcHogeApp_v3」をダウンロードしてください。サイズを抑えるため、アセンブリ類は削除してあるのでVisual Studio 12 Express for Webがインストールされた環境で開いてください。