いきなりですが
簡単なサンプルを。
class Program
{
static void Main(string[] args)
{
var animal = new Wankuma();
animal.Cry();
Console.ReadLine();
}
}
public class Wankuma
{
public void Cry()
{
Console.WriteLine("クマ~");
}
}
これを実行すると
と表示されます。
呼び出されたメソッドをログに残したい!
私が携わったプロジェクトでは、業務アプリではログを出力する場面がほとんどでした。でもログを出力するコードで汚されるのは嫌…。
そこで Policy Injection Application Block の出番
Policy Injection Application Block (以下 PIAB)はアスペクト指向によるソフトウェア開発を可能にするアプリケーションブロックです。PIAB を使う事で、ビジネスロジックなどの本来的な機能から、ロギングや例外処理などの横断的な機能を分離し、外部から注入することができるようになります。
さっそく試してみます!
参照するアセンブリを追加
次の2つを追加します。
- Microsoft.Practices.EnterpriseLibrary.Common
- Microsoft.Practices.EnterpriseLibrary.PolicyInjection
CustomCallHandler を作成
[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;
}
}
イベントログにメソッド名を出力します。
冒頭のサンプルを書き変えます
class Program
{
static void Main(string[] args)
{
// PolicyInjection クラスを使って生成
var animal = PolicyInjection.Create<Wankuma>();
animal.Cry();
Console.ReadLine();
}
}
// MarshalByRefObject から派生させる
public class Wankuma : MarshalByRefObject
{
public void Cry()
{
Console.WriteLine("クマ~");
}
}
最後に構成ファイルを記述
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>
<policyInjection>
<policies>
<add name="Policy">
<matchingRules>
<!--Cry メソッドをインターセプトする-->
<add match="Cry" ignoreCase="false" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MethodSignatureMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="Method Signature Matching Rule" />
</matchingRules>
<handlers>
<!--自作の Callhandler を指定-->
<add order="0" type="PIABSample.EventLogCallHandler, PIABSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
name="Custom Handler" />
</handlers>
</add>
</policies>
</policyInjection>
</configuration>
Cry メソッドをインターセプトして、前後でログを出力します。
実行結果
メソッド呼び出し前と後の、2つのログが出力されています。
まとめ
見ての通り、横断的な機能(ここではロギング)が本来的な機能(鳴き声出力)から分離されているため、スッキリしたコードになりました。