かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

EnterpriseLibrary 6のException Handling Application Block

暫くストアアプリとかユニバーサルアプリとか見てたらEnterprise Libraryって6になってたんですね。

例外処理は、アプリケーションの花形!(コード量的に)なので、そこをサポートしてくれる基盤はきっちり作らないといけない。ということで、Enterprise LibraryにもException Handlling Application Blockというかたちで例外処理の部品がしっかり用意されてます。

使い方

Enterprise Library 6になって、コードでかちっと簡単に構成してインスタンス化が出来るようになったみたいですね。流れるようなインターフェースや、DIコンテナ中心の考え方はさようなら。それはそれでさみしい気はするけど、使おうと思えば使えるからいいよね。新しい書き方になれませふ。

Exception Handling Application Blockでは、IEnumerableをもとにExceptionManagerのインスタンス化が出来るようになってる。

var exManager = new ExceptionManager(new[]
{
    // ExceptionPolicyDefinitionの定義
});

ExceptionPolicyDefinitionでは、定義の名前と、どの例外をどんなふうに処理するのかを表すExceptionPolicyEntryの列挙を使って作ることができる。

ExceptionPolicyEntryは、処理する例外の型と、例外を処理した結果どうするのかというのを表すPostHandlingAction列挙体と、例外の処理を表すIExcpeitonHandlerの列挙で作ることができる。PostHandlingActionは以下の値を持ってる。

  • None: なにもしない
  • NotifyRethrow: 再度例外を投げることを通知する
  • ThrowNewException: 新しい例外を投げる

IExceptionHandlerは、例外を置き換えるReplaceHandlerや、例外をラップするWrapHandlerなんかがある。Logを処理するLogging Application Blockと連携するLoggingExceptionHandlerなんかも、用意されてたりするし、自分でIExcpetionHandlerを実装するのもあり。IExceptionHandlerは、メソッドが1つしかないので以下のように簡単に定義できる。

class MyExceptionHandler : IExceptionHandler
{
    public Exception HandleException(Exception exception, Guid handlingInstanceId)
    {
        Console.WriteLine("はんどりんぐしてログとった");
        return exception;
    }
}

長くなったけど、結局、DivideByZeroExceptionはログをとって続きの処理をやって、ExceptionはMyExceptionにラップして再度投げるという定義は以下のようになる。

var definitions = new List<ExceptionPolicyDefinition>
{
    new ExceptionPolicyDefinition("default", new[]
    {
        new ExceptionPolicyEntry(
            typeof(DivideByZeroException),
            PostHandlingAction.None,
            new[]
            {
                new MyExceptionHandler()
            }),
        new ExceptionPolicyEntry(
            typeof(Exception),
            PostHandlingAction.ThrowNewException,
            new[]
            {
                new WrapHandler("wrap", typeof(MyException))
            }),
    })
};

var exManager = new ExceptionManager(definitions);

ExceptionManagerにはProcessというメソッドが定義されていてラムダ式と、ExceptionPolicyDefinitionの名前を渡して呼び出すことができる。戻り値がある版(Func)と無い版(Action)があって、割といい感じになってると思う。

以下のような感じで使える.

// ラップしてスローしてくれる
exManager.Process(() =>
{
    Console.WriteLine("throw new Exception()");
    throw new Exception();
}, "default");

Processで例外のリスローする場合はExceptionPolicyEntryでThrowNewExceptionを指定しておかないとダメみたい。NotifyRethrowとかは、HandleExceptionという、もっと細かな制御ができるメソッドで使う。

try
{
    // 例外が発生する可能性のあるコード
}
catch (Exception ex)
{
    Exception outEx = null;
    if (exManager.HandleException(ex, "default", out outEx))
    {
        throw outEx;
    }
    else
    {
        Console.WriteLine("例外投げなくていい");
    }
}

out引数を受け取らないバージョンのHandleExceptionメソッドもあって、こっちは例外投げないといけないときは勝手に投げてくれるらしい。

他のやつも今後みていってみようかなぁ。