Unity と PIAB を連携させる

はじめに

Unity と Policy Injection Application Block (以下 PIAB) と連携させてみました。

カスタム Callhandler とカスタム HandlerAttribute

[ConfigurationElementType(typeof(CustomCallHandlerData))]
public class EventLogCallHandler : ICallHandler
{
    public int Order { get; set; }

    public EventLogCallHandler(NameValueCollection attributes)
    {
        // このコンストラクタは必須みたい
    }

    private const string SOURCE = "PIABSample";

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        // メソッド呼び出し前にログを出力
        EventLog.WriteEntry(SOURCE,
            string.Format("{0} メソッドを呼び出します。", input.MethodBase.Name),
            EventLogEntryType.Information);

        // メソッド呼び出し
        IMethodReturn result = getNext()(input, getNext);

        // メソッド呼び出し後にログを出力
        if (result.Exception != null)
        {
            // 例外が発生したとき
            EventLog.WriteEntry(SOURCE,
            string.Format("{0} メソッド呼び出しで例外が発生しました。", input.MethodBase.Name),
            EventLogEntryType.Error);
        }
        else
        {
            EventLog.WriteEntry(SOURCE,
            string.Format("{0} メソッドを呼び出しました。", input.MethodBase.Name),
            EventLogEntryType.Information);
        }

        return result;
    }
}

public class EventLogCallHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler()
    {
        return new EventLogCallHandler();
    }
}

PIAB で属性を使ってインターセプト対象メソッドを指定する』のエントリで作成したものを流用します。

DI で使用するインタフェースとクラスを用意

public interface IAnimal
{
    // Cry メソッド呼び出し時にイベントログを出力する
    [EventLogCallHandler]
    void Cry();
}

public class Wankuma : IAnimal
{
    public void Cry()
    {
        Console.WriteLine("クマ〜");
    }
}

public class Person
{
    [Dependency]
    public IAnimal Pet { get; set; }

    public void CallPet()
    {
        Pet.Cry();
    }
}

毎度おなじみのインタフェースとクラスです^^;

PIAB と Unity を連携させるために UnityContainerExtension を作成

ここからが本番。

public class EventLogExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        // ここで登録した PolicyIjectionPolicy は次に登録する
        // PolicyInjectionStrategy で使われます。
        Context.Policies.SetDefault<IPolicyInjectionPolicy>(new PolicyInjectionPolicy(true));
        
        // Unity がインスタンスを生成するパイプラインの PreCreation ステージで
        // PolicyInjectionStrategy の処理が実行されるように登録します。
        Context.Strategies.AddNew<PolicyInjectionStrategy>(UnityBuildStage.PreCreation);
    }
}

PolicyInjectionStrategy は登録した PolicyInjectionPolicy を使って、Unity が生成したインスタンスに PIAB のポリシーを適用します。つまり RealProxy の派生クラスでラップする。この辺りは『PIABの中身を見てみた』のエントリを見て下さい。
Strategies や Policies がどう利用されるのかについては、後でエントリにする予定です。

作成した UnityContainerExtension を使ったコードに修正

static void Main(string[] args)
{
    // DI コンテナ取得
    UnityContainer container = new UnityContainer();

    // DI コンテナを拡張
    container.AddNewExtension<EventLogExtension>();

    // 型を登録
    container.RegisterType<IAnimal, Wankuma>();

    // インスタンスを取得
    Person person = container.Resolve<Person>();

    // ペットを呼び出す
    person.CallPet();

    Console.ReadLine();
}

作成した EventLogExtension を使って、UnityContainer を拡張しています。
このコードを実行すると、CallPet メソッド内で Cry メソッドが呼ばれる前後にイベントログが出力されます。

まとめ

Unity と PIAB の連携は、UnityContainer のインスタンス生成処理のパイプラインに、ポリシーを適用する処理を組み込むための UnityContainerExtension を作成することで実現可能です。