Enterprise Library 4.1が出てた。
気になるのは、お気に入りのUnityContainerです。バージョンが1.2になるらしい。
その中でも、Policy Application Blockみたいな動きをするInterceptionというのが気になったのでつっついてみた。
AOPといえばロギング!!ということで、ロギングを例にしてみようと思う。まずは、土台作りから。
いつものに、ちょっぴり足したPersonクラスを作る。
namespace UnityEducation.Entities
{
/// <summary>
/// 挨拶できるよ
/// </summary>
public interface IGreeter
{
/// <summary>
/// 挨拶を返すよ
/// </summary>
/// <returns>挨拶の文字列</returns>
string Greet();
}
/// <summary>
/// いつもの人間クラス
/// </summary>
public class Person : IGreeter
{
/// <summary>
/// 名前あるよ
/// </summary>
public string Name { get; set; }
/// <summary>
/// 挨拶するよ
/// </summary>
/// <returns>名前入りの挨拶!</returns>
public string Greet()
{
return string.Format("{0}です!こんにちは!", Name);
}
}
}
このGreetメソッドにログを仕込むのが最終目標になる。
続いてMainメソッドで、単純にUnityContainerにPersonを登録して使うところの処理を書く。
とりあえず、UnityContainerとInterceptionを使うのでObjectBuilder2とUnityとUnity.InterceptionExtensionをプロジェクトの参照に追加する。
そしてMainメソッドに処理を書いていく。
using System;
using Microsoft.Practices.Unity;
using UnityEducation.Entities;
namespace UnityEducation
{
class Program
{
static void Main(string[] args)
{
IUnityContainer c = new UnityContainer();
// インスタンスをコンテナに登録
c.RegisterInstance<IGreeter>(new Person { Name = "大田" });
// インスタンスをコンテナから取得して挨拶文を表示
var ohta = c.Resolve<IGreeter>();
Console.WriteLine(ohta.Greet());
}
}
}
これを実行すると、下のようになる。
やっと土台が完成。これに処理を挟み込んでみようと思う。処理を挟み込むためには、2つのクラスを作らないといけない。
- HandlerAttributeを継承した属性
メソッドとハンドラを結びつけるための目印
- ICallHandlerを継承したクラス
挟み込む処理
まずは属性から。
/// <summary>
/// ログを出力するためのAttribute
/// </summary>
public class LoggingAttribute : HandlerAttribute
{
/// <summary>
/// 挟み込む処理を返す
/// </summary>
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new LoggingHandler();
}
}
これは、CreateHandlerメソッドをオーバーライドするだけでOK。返すものは、この後作るハンドラです。
ということで、ハンドラを作成する。
/// <summary>
/// ログを出力するハンドラ
/// </summary>
public class LoggingHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("処理開始");
try
{
// 処理を呼ぶ
return getNext().Invoke(input, getNext);
}
finally
{
Console.WriteLine("処理終了");
}
}
public int Order { get; set; }
}
ICallHandlerを継承して、Invokeメソッドを実装するだけでOK。最初と最後に処理の開始と終了のメッセージを出すようにしてみた。
実際のGreetメソッドの処理は、getNextデリゲートを呼ぶことで取得できる、getNext()の戻り値に対してInvokeメソッドを呼ぶと本来走る処理が実行される。(同じメソッドに複数のHandlerをセットしてたら別のHandlerの処理が呼ばれる)
そして、PersonクラスのGreetメソッドにLoggingAttributeをセットする。
/// <summary>
/// いつもの人間クラス
/// </summary>
public class Person : IGreeter
{
/// <summary>
/// 名前あるよ
/// </summary>
public string Name { get; set; }
/// <summary>
/// 挨拶するよ
/// </summary>
/// <returns>名前入りの挨拶!</returns>
[Logging] // ログの処理を入れるよ
public string Greet()
{
return string.Format("{0}です!こんにちは!", Name);
}
}
最後にUnityContainerに対してInterceptionを使うということを示すコードを追加する。
class Program
{
static void Main(string[] args)
{
IUnityContainer c = new UnityContainer();
// Interceptionの拡張を追加
c.AddNewExtension<Interception>();
// Interceptionの設定
c.Configure<Interception>().
// IGreeterにはTransparentProxyInterceptorを使います
SetInterceptorFor<IGreeter>(new TransparentProxyInterceptor());
// インスタンスをコンテナに登録
c.RegisterInstance<IGreeter>(new Person { Name = "大田" });
// インスタンスをコンテナから取得して挨拶文を表示
var ohta = c.Resolve<IGreeter>();
Console.WriteLine(ohta.Greet());
}
}
これを実行すると、さっきまでの挨拶一行以外に、処理開始と処理終了も出力されるようになる。
いい感じだ。
参考:http://www.pnpguidance.net/Screencast/UnityInterceptionExtensionRemotingPolicyInjectorScreencastUnityTutorials.aspx