miso_soup3 Blog

主に ASP.NET 関連について書いています。

Project SilkにならってUnityを使ってみる

はじめに

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-4.リゾルバの設定

独自で実装したリゾルバを、MVCが使ってくれるように設定します。
Global.asax.cs に以下のコードを追加します。

2-5.設定終了

以上で設定は終了です。
WebConfigに型と具象クラスのマッピングを書き、
Controllerのコンストラクタの引数に定義するだけで、
コントローラへの依存注入は完了です。

3.WebConfigとは別のファイルに記述する。

Silkで行っているように、Unityの設定をWebConfig上ではなく別ファイルに分割して記述すると、分かりやすいです。
デバッグ時とリリース時で切り替えるときに便利です。

3-1.Unity用の設定ファイルを用意する

3-2.WebConfigの記述を変更する

4.ServiceLocatorを利用する

コンストラクタによって依存性の注入を行った場合、
引数がどえらいことになるかもしれません。

このようなことになりたくない場合は、ServiceLocatorを利用する方法もあります。

4-1.ServiceLocatorとは

ServiceLocatorはパターンの一種です。
ServiceLocatorオブジェクトは、アプリケーションが必要になるサービスの取得方法をすべて知っています。

なのでControllerはServiceLocatorに「このサービスください」と頼むことができます。

Unityには、IServiceLocatorインターフェイスと実装クラスがすでに用意されているので、
それらを利用します。

4-2.UnityのIServiceLocatorの利用方法

UnityのIServiceLocatorを利用するには、glocal.asax.csで以下のように記述します。

IServiceLocatorを利用したいコントローラに、コンストラクタを利用して注入します。

すると・・・
コントローラは、IServiceLocatorのGetInstanceメソッドから、
利用したい外部コンポーネントを取得することができます。

Project Silkでは、こんな風にちょっとオシャレです。

5.ServiceLocator と Dependency Injection

SilkのControllerでは、下のように、コンストラクタで注入されたサービスと、
IServiceLocatorから取得するサービス、2つの方法よりサービスを利用しています。

また、コンストラクタで注入されたサービスへは、あくまでもインターフェイスへ対話しているのに対して、
IServiceLocatorから取得しているサービスは、インターフェイスではなく直接実装クラスと対話しています。

この2つの違いは、何なのでしょうか?

2つのサービスの仕事内容を見てみるとなんとなく違いがわかる・・・・かもしれません。
1つ私が思うのは、コントローラのアクションメソッドがサービスへ要求する内容は、我が儘な場合がある、いうことです。

参考サイト

ServiceLocator と Dependency Injection、この2つのパターンについて、
とてもおもしろく&わかりやすく説明されている文章(翻訳サイト)がありました。
以下、一部引用です。

両者のいずれを選択するかはさしたる問題ではない。 「設定を利用から分離する」原則こそが重要なのだ。
Dependency Injection と Service Locator は必ずしも相互排他的なコンセプトであるというわけではない。
思うに、サービスが簡単にスタブ化できないという考えは、プロジェクトでそうできるような努力をしていないことから出てくるのではなかろうか。

Inversion of Control コンテナと Dependency Injection パターン

かっこいい!

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がどんな働きをしているのかを理解すると、コントローラの設計がどのようになっているか確認しやすくなると思います。