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
さんのコメント: さんのコメント: