はじめに
MVCのサンプルプロジェクトであるProject Silkでは、
UnityというIoCフレームワークを利用して関心の分離を行っています。
今回は、Silkのソースを参考にしながら、Unityの利用方法を記載したいと思います。
- 1.Unityとは
- 2.Unityの利用方法
- 3.WebConfigとは別のファイルに記述する。
- 4.ServiceLocatorを利用する
- 5.ServiceLocator と Dependency Injection
- 6.ASP.NET Web APIでは・・・・
よく張りしすぎてこんな目次になってしまいました。
1.Unityとは
ある部品に依存性を注入する方法の1つとして、
下のように、コンストラクタによる依存注入があります。
(Dependency Injectionパターンのコンストラクタインジェクションです)
この時、開発者は2つの作業が必要になると思います。
これらの作業は、IoCフレームワークを利用することで、すばやく行うことができます。
Unityは、そのIoCフレームワークの1つで、Microsoftから提供されています。
Unityの他には、StructureMap,Autofacなどがあります。
2.Unityの利用方法
2-1.インストール
MVCプロジェクトを作成後、Nugetでインストールします。
install-package Unity
- Microsoft.Practices.ServiceLocation.dll
- Microsoft.Practices.Unity.Configuration.dll
- Microsoft.Practices.Unity.dll
この3つの参照が追加されます。
2-2.WebConfig設定
”この型のときは、この具象クラスを利用する” という設定を行います。
WebConfigに、以下のものを追加します。
<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="UnityTrain.Web" /> <namespace name="UnityTrain.Web.Services" /> <assembly name="UnityTrain.Web" /> <container> <register type="IPersonService" mapTo="PersonService" /> </container> </unity>
上の例ではIPersonServiceには、PersonServiceのインスタンスを、と指定しています。
必要に応じて、namespaceを追加します。
Project Silkでは、下のように名前を省略せずに書いてありました。
<type type="MileageStats.Domain.Contracts.IUserServices, MileageStats.Domain" mapTo="MileageStats.Domain.UserServices, MileageStats.Domain">
2-3.リゾルバの実装
※Unity.Mvc3を使うと、実装の必要はありません。2012/07/17 Unityについて追記。Unity.MVC3がありました
MVCでは、あるインスタンスが欲しいとき、IDependencyResolverから取得しています。
デフォルトではそれを実装したDefaultDependencyResolverが、実際にインスタンスを提供しているのですが、
その中身はこうなっています。
public object GetService(Type serviceType) { try { return Activator.CreateInstance(serviceType); } catch { return null; } }
デフォルトのままでは、先ほどのWebConfigで指定したインスタンスを取得してくれないので、
IDependencyResolverを独自に実装する必要があります。
Project Silkでは、UnityDependencyResolverクラスがそれにあたります。
ので、そのクラスをそのまま追加します。
2-5.設定終了
以上で設定は終了です。
WebConfigに型と具象クラスのマッピングを書き、
Controllerのコンストラクタの引数に定義するだけで、
コントローラへの依存注入は完了です。
3.WebConfigとは別のファイルに記述する。
Silkで行っているように、Unityの設定をWebConfig上ではなく別ファイルに分割して記述すると、分かりやすいです。
デバッグ時とリリース時で切り替えるときに便利です。
4.ServiceLocatorを利用する
コンストラクタによって依存性の注入を行った場合、
引数がどえらいことになるかもしれません。
このようなことになりたくない場合は、ServiceLocatorを利用する方法もあります。
4-1.ServiceLocatorとは
ServiceLocatorはパターンの一種です。
ServiceLocatorオブジェクトは、アプリケーションが必要になるサービスの取得方法をすべて知っています。
なのでControllerはServiceLocatorに「このサービスください」と頼むことができます。
Unityには、IServiceLocatorインターフェイスと実装クラスがすでに用意されているので、
それらを利用します。
5.ServiceLocator と Dependency Injection
SilkのControllerでは、下のように、コンストラクタで注入されたサービスと、
IServiceLocatorから取得するサービス、2つの方法よりサービスを利用しています。
また、コンストラクタで注入されたサービスへは、あくまでもインターフェイスへ対話しているのに対して、
IServiceLocatorから取得しているサービスは、インターフェイスではなく直接実装クラスと対話しています。
この2つの違いは、何なのでしょうか?
2つのサービスの仕事内容を見てみるとなんとなく違いがわかる・・・・かもしれません。
1つ私が思うのは、コントローラのアクションメソッドがサービスへ要求する内容は、我が儘な場合がある、いうことです。
参考サイト
ServiceLocator と Dependency Injection、この2つのパターンについて、
とてもおもしろく&わかりやすく説明されている文章(翻訳サイト)がありました。
以下、一部引用です。
両者のいずれを選択するかはさしたる問題ではない。 「設定を利用から分離する」原則こそが重要なのだ。
Inversion of Control コンテナと Dependency Injection パターン
Dependency Injection と Service Locator は必ずしも相互排他的なコンセプトであるというわけではない。
思うに、サービスが簡単にスタブ化できないという考えは、プロジェクトでそうできるような努力をしていないことから出てくるのではなかろうか。
かっこいい!
6.ASP.NET Web APIでは・・・・
Web APIでも、IDependencyResolverを使うことができます。
が、MVCとは全く違います。
↓APIの方では・・・
設定方法も大分違います。
Using the Web API Dependency Resolver
Web Api (REST サービス) における IoC (関心事の分割)
7.おしまい
以上、Project Silkにならい、Unityを利用する方法を記載しました。
Unityがどんな働きをしているのかを理解すると、コントローラの設計がどのようになっているか確認しやすくなると思います。