何となく Blog by Jitta
Microsoft .NET 考

目次

Blog 利用状況
  • 投稿数 - 761
  • 記事 - 18
  • コメント - 35961
  • トラックバック - 222
ニュース
  • IE7以前では、表示がおかしい。div の解釈に問題があるようだ。
    IE8の場合は、「互換」表示を OFF にしてください。
  • 検索エンジンで来られた方へ:
    お望みの情報は見つかりましたか? よろしければ、コメント欄にどのような情報を探していたのか、ご記入ください。
It's ME!
  • はなおか じった
  • 世界遺産の近くに住んでます。
  • Microsoft MVP for Visual Developer ASP/ASP.NET 10, 2004 - 9, 2011
広告

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

その他

わんくま同盟

同郷

 

ネタもと:中の技術日誌ブログ「もうちょっと具体的に

ただ登録処理の伝票名称の重複は不可となっている場合にはどうしましょう。

この重複不可エラーはもちろん業務的なエラーですから、アプリケーションを落としてはいけません。

方法としては、例外、クラス化などがあるでしょう。

int 登録処理(int 得意先ID, string "伝票名称") {
  try {
    return (int)com.execute();
  } catch (SQLException ex){
    if ( ex.Number = 2726 ) {
      throw new KeyDuplicationException();
    } else {
      throw ex;
    }
  }
}
int 登録伝票ID;
エラーコード _error;
bool 登録処理(int 得意先ID, string "伝票名称") {
  try {
    登録伝票ID = com.execute();
    return true;
  } catch (SQLException ex){
    if ( ex.Number = 2726 ) {
      this._error = エラーコード.KeyDuplicate;
      return false;
    } else {
      throw ex;
    }
  }
}

コメントにしようかと思ったけど、トラックバックで。

やっぱり、上ですかね。

.NET Framework の推奨事項として、「例外は使わない方向で」というのがあります。中さんも、「業務エラー」という言葉を出していますが、「業務上、想定されるものなのか」ということを、まず考える必要があります。想定されるなら、戻り値として表現します。

ただし、私は、もう一歩、考えてみたいと思っています。

アプリケーションとしては「想定されている」けれど、メソッドとして「想定しなければならないのか」、ということ。

この場合、重複エラーは、メソッドとして想定しなくて良い、と考えます。UI や事前チェックで弾くべし。あるいは、どこで業務エラーに読み替えるべきか、と言いましょうか。

さて、呼び出し側を考えます。というか、元々「戻り値を捨ててはいけない」という話なので、呼び出し側を抜きにして考えてはいけないと思います。


int 伝票ID;
try {
    伝票ID = 登録処理(100, "新発売のWindows Vistaについて");
    DataRow dr = 取得処理(伝票id); // catch ブロックによっては try ブロックの外
} catch (KeyDuplicationException ex) {
    // 重複登録のみ、何らかの処理が必要
    必要な処理
} finally {
    必要な処理
}

// 例外も発生するので、場合によっては try - finally で括る
if (登録処理(100, "新発売のWindows Vistaについて") == false) {
    if (this._error == エラーコード.KeyDuplicate) {
        重複処理で必要な処理
    } else {
        その他のエラーで必要な処理
    }
    return などの処理
}
DataRow dr = 取得処理(伝票id);

下のコードで、this._error の判定ブロックにおける else ブロックは必要でしょうか?登録処理メソッドの内容を知っていれば、不要です。知らなければ、必要です。つまり、下のコードでは、ライブラリ ユーザがライブラリの中身を知っている必要があります。私は、そのような作り方は、あまり良くないと考えます。これは、戻り値が「エラーコード」列挙体であれば、また別ですが。しかし、MFC(Win32API?)の ERROR_SUCCESS には違和感を感じるので、同じように「エラーコード.NoError」とかも、違和感があるけど。

また、元々、「戻り値を捨ててはいけない」という話です。ここを考えると、下の方は、このような書き方も出来ます。そしてこれは、戻り値を捨てていることになってしまいます。よって、元のお題を満足しません。


登録処理(100, "新発売のWindows Vistaについて")
if (this._error == エラーコード.KeyDuplicate) {
    重複処理で必要な処理
} else if (this._error == ...) {
    その他のエラーで必要な処理
} else {
    // エラー無し
    DataRow dr = 取得処理(登録伝票ID);
}

じゃぁ、上のコードはどうか。重複しているという例外が上がってきますが、それを処理する必要がなければ、キャッチしなければいい。もし、例外の種類が増えても、業務エラーへの読み替え処理が必要な例外だけキャッチします。また、このアプリケーション固有の例外を MyApplicationException クラスを作ってそこから派生させておけば、一括した処理も可能です。


最後に。微細なところに突っ込んでおきます。コメントなら書かないんだけどね。私のブログだもん(^-^;

int 登録処理(int 得意先ID, string "伝票名称") {
  try {
    return (int)com.execute();
  } catch (SQLException ex){
    if ( ex.Number = 2726 ) {
      throw new KeyDuplicationException(
          "伝票名にはユニーク インデックスが作成されています。", ex);
    } else {
      throw;
    }
  }
}

catch ブロックの中で throw ex と書くと、この例外を投げた場所が、この行になってしまいます。実際には、com.execute() メソッドの中のはずなのに。そういうわけで、throw とのみ、書きます。

例外を生成するときは、例外メッセージを書くことが推奨されています(どこに書いてあったかは、忘れた)。また、InnerException に、元々の例外を記録しておきましょう。なお、ここに書いた例外メッセージは、エンド ユーザに見せるためのものではありません。反対に、Exception.ErrorMessage を、エンド ユーザに見せるような作りは控えましょう。


追加:事前チェックをする場合

// 事前チェックを行う
エラーコード errorCode;
try {
    データベースをロックする処理
    if (Is登録可能(100, "新発売のWindows Vistaについて", out errorCode) == true) {
        int 伝票ID;
        伝票ID = 登録処理(100, "新発売のWindows Vistaについて");
        DataRow dr = 取得処理(伝票id);
    } else {
        エラー処理(errorCode);
    }
} finally {
    ロックを解除する処理
}
投稿日時 : 2007年1月15日 22:01
コメント
  • # re: もうちょっと具体的に
    中の技術日誌ブログ
    Posted @ 2007/01/15 22:17
    re: もうちょっと具体的に
  • # re: Re:もうちょっと具体的に
    えムナウ
    Posted @ 2007/01/15 22:38
    大体私の言いたいことと同じですね。

    >UI や事前チェックで弾くべし。
    これだけは使える局面が狭すぎて嫌です。
    複数人数が使う業務を考えていないからです。

    UI や事前チェックで判定して、なおかつ、エラーハンドリングするというのもやりすぎな感じがします。

    私が好みなのはcom.execute()でストアドをよんでそのストアドのエラー値をRETURN_VALUEで拾うやり方です。
    ストアド内でTransactionしていれば完璧に複数人数が使う業務に耐えられますし、SQLサーバーや.NetFrameWorkのException関係のコストも軽減できます。
  • # re: Re:もうちょっと具体的に
    Jitta
    Posted @ 2007/01/16 21:45
    えムナウさん、コメントありがとうございます。

    > UI や事前チェックで判定して、なおかつ、エラーハンドリングするというのもやりすぎな感じがします。
     エラー ハンドリングは、一番下でちょこっと書いていますが、エンド ユーザ用ではなく、開発者用、バグ発見用です。つまり、「出してはいけない」のです。あるいは、ASSERT と言い換えましょうか。
     UI や事前チェックを行ってから、呼び出さなければならないのなら、ライブラリ ユーザ(つまり、開発者)に一定の手順でコーディングすることを強います。つまり、「事前チェックをしてから、登録処理をする」という手順ですね。この手順に従っていないことを、開発者に知らせる必要があると考えます。
     あ、呼び出し側のエラー ハンドリング!?あ・・・例が抜けてますね。付け加えます(^-^;
    (ハンドリングだから、呼び出し側じゃないかorz)

    > ストアドをよんでそのストアドのエラー値をRETURN_VALUEで拾うやり方です。
    ん。。。ストアドは、ちょっと、考え中。
  • # re: Re:もうちょっと具体的に
    えムナウ
    Posted @ 2007/01/17 2:43
    複数人数が使う業務はUI や事前チェックで判定しても、
    実際にDBの登録するときまで時間があるので、
    同時登録した場合DBの登録でエラーが発生することがあります。

    その同期のためにもTransactionがあるんですが、
    これはこのケースの場合ストアド内のTransactionで、
    処理したいなということなんです。
  • # re: Re:もうちょっと具体的に
    ちゃっぴ
    Posted @ 2007/01/17 23:09
    個人的には、例外は例外なんですけど。

    とある実行するという method があるとして、
    それが実行できなかった場合というのは、
    素直に例外として処理すべきだと思います。

    処理結果が正常かどうかを戻り値で戻すのは
    どちらかというと邪道かな。
    # それしか方法が無い場合は除く

    ただし、multi thread で無いことが保障されている
    または、その method で扱うものがその method 内で
    しか扱われないことが保障されている場合には、
    当然ですが、check method を作って、method を
    呼び出す前に確認してから実行します。
  • # re: Re:もうちょっと具体的に
    Jitta
    Posted @ 2007/01/21 21:40
    ちゃっぴさん、コメントありがとうございます。

    > 個人的には、例外は例外なんですけど。
    あ・・・まぁ・・・いや、そういわれると、そうなんですけど(^-^;アセアセ

     なんて言うんだろう。「アプリケーションとして、例外的状況」と、「メソッドとして、例外的状況」は、違うと思います。
     たとえば、LAN ケーブルが切れているなどでデータベース サーバとアクセスできない。これは、「アプリケーションとして例外的状況」だと思います。
     しかし、「アカウント ID とパスワードが一致するアカウント情報を取得する」という操作において、「一致するアカウント情報がない」という状況は、メソッドとしては例外的状況だけれど、アプリケーションとしては考慮されるべき状況だと思います。
     この様な、例外を読み替える操作というのは、必要ではないかと思います。
  • # re: Re:もうちょっと具体的に
    ちゃっぴ
    Posted @ 2007/01/22 21:41
    > なんて言うんだろう。「アプリケーションとして、
    > 例外的状況」と、「メソッドとして、例外的状況」は、
    > 違うと思います。

    そういう風に作っているとその method の汎用性が
    どんどんなくなっていくような気がしますけど。

    > しかし、「アカウント ID とパスワードが一致する
    > アカウント情報を取得する」という操作において、・・・

    この例ならば、「アカウント情報を取得する」という
    method なので取得できない場合は、Null を返すとかが
    適切でしょうが、単に「logon」という method ならば
    logon できないのは例外として処理しますね。

    では、なぜ例外として処理するかというと、利用する
    application によっては logon できないことが致命的かも
    しれないし、そうでないかもしれない。

    また、現状では致命的ではないかもしれないが将来はどう?
    というのもあると思います。

    で、なぜ例外にするかというと例外だとその method を
    呼び出す method のさらにその読み出し元からでも
    制御ができるからです。

    まあ、というのはあくまでも理想で例外に伴う overhead が
    我慢ならない場合、要するに tuning を必要とする場合には
    当然こういう実装はしないでしょうが。

    個人的には順序が逆なのでは?という気がします。
タイトル
名前
Url
コメント