Enterprise Library に含まれる Unity を使用することで、インスタンスの作成や既存のインスタンスに対して 依存性の注入 (Dependency Injection) を行うことができます。今回は Unity の使い方のメモとして、Constructor Injection や Property Injection, Methoc call Injection を行う方法をサンプルプログラムとともに記載します。
Unity 2.0 の全体的なヘルプは 下記オンラインのURLやEnterprise Library の codeplex のページから参照されている ヘルプ(chm)ファイルを参照して下さい。
http://msdn.microsoft.com/en-us/library/ff663144.aspx
次の環境で動作確認を実施しています
- Visual Studio 2010 Professional
- Enterprise Library 5.0
1. Unity のセットアップ
Unity のみを使用する場合は、 [C#] Unityでシングルトン で紹介しているように Unity と 依存性のあるアセンブリのみを NuGet から取得できます。Enterprise Library をインストールする場合は、Microsoft Enterprise Library 5.0 からダウンロードできます。
最新版は codeplex の Enterprise Library の URL から確認してください。
patterns & practices – Enterprise Library
http://entlib.codeplex.com/
2. 必要なdll をプロジェクトの参照に追加
NuGet 経由で Unity をセットアップしていない場合は、 プロジェクトにUnity の dll 参照を追加します。
必要なdll はMicrosoft.Practices.Unity.dll ですが、構成ファイルのUnity用の設定をプログラムで取得したり、Interceptionを使用する場合などは必要に応じてほかのdllをプロジェクトの参照に追加してください。どの場合に何のdllが必要かは下記UnityのヘルプのURLの先頭付近に記載されています。
Adding Unity to Your Application
http://msdn.microsoft.com/en-us/library/ff660927(PandP.20).aspx
以降では、それぞれの Injection を使用するサンプルプログラムを掲載しますが、サンプル内で使用されているクラスを先に記載しておきます。
class SampleBase { public string WhichConstructorCalled { get; set; } } class Sample1 : SampleBase { /// <summary> /// コンストラクタが複数ある場合は、 /// InjectionContainerAttribute /// でどのコンストラクタを使用するかを指定する /// </summary> [InjectionConstructor] public Sample1() { Console.WriteLine("引数なし"); this.WhichConstructorCalled = "既定のコンストラクタ"; } /// <summary> /// コンストラクタ /// </summary> /// <param name="name"></param> public Sample1(string name) { Console.WriteLine(name); this.WhichConstructorCalled = "nameを引数にとるコンストラクタ"; } public Sample1(string name1, string name2) { Console.WriteLine(name1 + name2); this.WhichConstructorCalled = "nameを引数にとるコンストラクタ1"; } } class Other1 { public SampleBase Sample1 { get; set; } public SampleBase Sample2 { get; set; } } class Other2 { [Dependency] public SampleBase Sample1 { get; set; } [OptionalDependency] public SampleBase Sample2 { get; set; } } class MethodInj { public MethodInj() { Console.WriteLine("コンストラクタ called"); } [InjectionMethod] public void Initialize() { Console.WriteLine("Injection is called"); } public void Initialize2() { Console.WriteLine("Injection2 is called"); } }
DependencyAttribute や InjectionConstructorAttribute , InjectionMethodAttribute はUnityContainer に対して InjectionMember から派生した インスタンス を指定しない場合の既定の振る舞いに影響を当てます。詳細は下記ヘルプを参照
Annotating Objects for Constructor Injection
http://msdn.microsoft.com/en-us/library/ff660875(PandP.20).aspxAnnotating Objects for Property (Setter) Injection
http://msdn.microsoft.com/en-us/library/ff660903(PandP.20).aspxAnnotating Objects for Method Call Injection
http://msdn.microsoft.com/en-us/library/ff660873(PandP.20).aspx
3.Constructor Injection を使用してみる
インスタンス作成時にコンストラクタに対して インジェクション を行う、Constructor Injection を使用する サンプルプログラムを記載します。InjectionConstructor を指定して コンストラクタの設定値を指定したり、 ParameterOverride を Resolve メソッドに指定して、設定値を上書きして解決を行っています。また、 InjectionConstructor Attribute がクラスに付与されている場合、そのコンストラクタが解決時に使用されます。
static void ConstructorInjectionTest() { IUnityContainer container = new UnityContainer(); // コンストラクタが1つの場合そのコンストラクタを使用 // 複数ある場合は InjectionConstructorAttribute をコンストラクタ // に付与することで使用するコンストラクタを指定できる container.RegisterType<SampleBase, Sample1>(); SampleBase s = container.Resolve<SampleBase>(); Console.WriteLine(s.WhichConstructorCalled); // InjectionConstructor を使用することで どのコンストラクタを使用するかを // 指定できる container.RegisterType<SampleBase, Sample1>(new InjectionConstructor("nameを引数にとるVersion")); s = container.Resolve<SampleBase>(); Console.WriteLine(s.WhichConstructorCalled); // コンストラクタのInjectionはResolverOverride を指定することで // Injectionの設定を上書きできる s = container.Resolve<SampleBase>(new ParameterOverride("name", "hello")); Console.WriteLine(s.WhichConstructorCalled); }
対象のクラスが複数のコンストラクタを持つ場合、 RegisterType メソッドで InjectionConstructor を指定していない場合に、UnityContainer は Resolve メソッドが呼び出されたときに、どのコンストラクタが使用するでしょうか。 UnityContainer のコンストラクタの選択基準については、Annotating Objects for Constructor Injection の"How Unity Resolves Target Constructors and Parameters" に説明が記載されています。簡単に記載すると次の通りです。
- InjectionConstructor Attribute が付与されている場合、そのコンストラクタを使用する
- InjectionConstructorAttribute が指定されていない場合、最も引数の多いコンストラクタを使用する
- もっとも引数の多いコンストラクタが複数ある場合、例外を発生させる
4.Property Injection を使用してみる
解決時に プロパティに対して インジェクションを Property Injection を行うサンプルを記載します。InjectionProperty を RegisterType 時に指定しない場合は、 Dependency Attribute を付与したプロパティに対して Injection が行われます。 またサンプルでは、 PropertyOverride を Resolve メソッド呼び出し時に 指定して InjectionProperty の設定を上書きする方法も記載しています。
static void PropertyInjectionTest() { IUnityContainer container = new UnityContainer(); container.RegisterType<SampleBase, Sample1>(); container.RegisterType<SampleBase, Sample1>("p", new InjectionConstructor(string.Empty)); container.RegisterType<Other1>(new InjectionProperty("Sample2")); Other1 o1 = container.Resolve<Other1>(); Console.WriteLine(o1.Sample2.WhichConstructorCalled); var t = container.Resolve<SampleBase>("p", new ParameterOverride("name", "hello")); o1 = container.Resolve<Other1>(new PropertyOverride("Sample2", t)); Console.WriteLine(o1.Sample1 == null); Console.WriteLine(o1.Sample2.WhichConstructorCalled); // Dependency がある場合はそのプロパティが解決される Other2 o2 = container.Resolve<Other2>(); Console.WriteLine(o2.Sample1.WhichConstructorCalled); }
DependencyAttribute は プロパティーだけでなく コンストラクタやメソッドの 引数に対して付与することができます。Unity Container の外部でインスタンス化されたオブジェクトに対して Property Injection を行う場合は、 BuildUp メソッドを使用してください。
5. Method Call Injection を使用してみる
Method Call Injection を設定すると Unity コンテナによって クラスがインスタンス化されたときに Method call Injection で指定したメソッドを呼び出されるようにすることができます。
RegisterInstance で インスタンスを登録していると Method call Injection で設定した メソッドは Resolve メソッド呼び出し時には呼び出されません。なぜなら初期済みだからです。
UnityContainer から作成していない オブジェクトに対して 依存性の注入 (Dependency Injection を行うために、 BuildUp メソッドを使用した場合、Unity コンテナに登録した型に対して Method call Injection が設定されていると 登録されている メソッドが呼び出されるので注意してください。
サンプルプログラムを記載します。
/// <summary> /// Injectionが実施されるときに /// 呼び出される /// </summary> static void MethodInjectionTest() { IUnityContainer container = new UnityContainer(); var v = container.Resolve<MethodInj>(); container.RegisterType<MethodInj>(new InjectionMethod("Initialize2")); v = container.Resolve<MethodInj>(); // BuildUp した時も Initialize2 が呼び出されるので注意 MethodInj i = new MethodInj(); container.BuildUp<MethodInj>(i); }
サンプルでは InjectionMethod を RegisterType 時に指定するか、 InjectionMethod Attribute を付与することで Method Call Injection が行われるようにしています。
6. まとめ
Unity を使用した Injection の説明は以上です。Unity を使用すると アプリケーション構成ファイルを使用して Injection の設定が行えます。詳細は ヘルプ を参照してください。また、 Unity の Interception を使用することで アスペクト指向 プログラミンを行えます。 詳細は Unity のヘルプや 下記 URL を参照してください。
アスペクト指向プログラミング、インターセプト、および Unity 2.0
http://msdn.microsoft.com/ja-jp/magazine/gg490353.aspx
さんのコメント: さんのコメント: