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