読者です 読者をやめる 読者になる 読者になる

【C#】OLE DB接続を使った時のエラー特定方法

SQLを動的に生成してOLE DB接続でSELECTを発行するものを作っていたんだけど、そのSQL実行時にある特定のエラーが出た時は別の方法で再試行したい、という案件。

とりあえずエラーが起きた時に返ってくるのはOleDbExceptionみたいなので、catchしてみよう。

using (var conn = new OleDbConnection(connectionString))
{
    try
    {
        conn.Open();
        using (var command = new OleDbCommand(query, conn))
        using (var reader = command.ExecuteReader())
        {
            //something
        }
    }
    catch (OleDbException ode)
    {
        //TODO: specify sql error
    }
}

OleDbExceptionのメンバを流し読みすると、ErrorCodeプロパティあたりがいいんじゃないかな、と思うのだけれど、このHRESULTは定数にもenumにもなっておらず、ブレークポイントをおいて実際にエラーを起こしてみてメモしておく、と言うダサすぎて憤死しかねない方法を取らなくてはならない。

で、代替案としてのErrorsプロパティ。これはOleDbErrorクラスのコレクションが入っている。

じゃあOleDbErrorクラスにはどんなメンバがあるのかなと見ていくと、SQLStateなるプロパティがある。
そう、ストアドプロシージャにおいて頻繁に見かけるアイツを取得できるプロパティがあるのである。

と言うわけで、OleDbExceptionからErrorsプロパティを取得し、ある特定のSQLStateを持つOleDbErrorがあるかどうかを探すコードを書いてみよう。

using (var conn = new OleDbConnection(connectionString))
{
    try
    {
        conn.Open();
        using (var command = new OleDbCommand(query, conn))
        using (var reader = command.ExecuteReader())
        {
            //something
        }
    }
    catch (OleDbException ode)
    {
        var errors = ode.Errors.Cast<OleDbError>();

        if (errors.Any(x => x.SQLState == ERROR_HOGE))
        {
            //alternative something
        }
        else
        {
            Console.WriteLine(ode.Message);
        }
    }
}

いやまぁ本当ならLINQ to SQLとかLINQ to Entity Frameworkとか使うべきで、こんなファッキンレガシーなコードを書くなって言いたいのかもしれないけどさ、この辺最初からジェネリックで作って欲しいよね。と思いながらさくさくっとOleDbException.Errorsの中身をCastし、これまたさくさくっとAnyで判定しましょう。

どういった場合になんてSQLStateが返ってくるのかはちゃんと標準規格で決まっており、探せば一覧も出てくるので予め必要そうなものはenumを作るなり定数化しておきましょう。型はstringです。だっせぇ。