いきなりですが
簡単なサンプルを。
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 メソッドをインターセプトして、前後でログを出力します。
まとめ
見ての通り、横断的な機能(ここではロギング)が本来的な機能(鳴き声出力)から分離されているため、スッキリしたコードになりました。