6

私は、バックエンドとして SQL サーバーを使用して C# で記述されたデータベース アプリケーションに取り組んでいます。また、データの整合性のために、データベース レベル (リレーション、チェック制約、トリガー) で可能な限り強化するようにしています。

それらが原因で、データに一貫性がない場合、保存/更新/挿入が失敗し、アプリが SqlException をスローする可能性があります。

UI (入力されたデータが無効な場合に意味のある情報をユーザーに提示するため) と、エラーをユーザーに提示する UI にエラーを報告する BL の両方で、さまざまな検証を行います。

ただし、実際にはアプリでチェックできず、データベースによって処理されるものがあります。つまり、カスケード削除がなく、ユーザーがマスター テーブルからエンティティを削除しようとすると、削除エラーが発生します。

たとえば、従業員テーブルは、従業員のマネージャー、部門のマネージャー、レジ担当者、チームリーダー、チームメンバーなど、多くの関係でマスターとして機能します。関係に関与していない新しい従業員を追加すると、それを削除できますが、ユーザーのそのような関係でマスターになっているものを削除しようとすると、DB レベルで適用される RI ルールが原因で (当然のことながら) 削除が失敗しますが、問題ありません。

try ... catch で削除コードを記述して例外を処理し、その従業員を削除できないことをユーザーに伝えます。しかし、ユーザーにもっと意味のある情報を提供したい - レコードを削除できない理由. おそらくそれは、テストチームにも追加されたテスト従業員の記録だったのでしょう。しかし、ユーザーはそれをどこに追加したかを忘れ、「チーム T1 の一部であるため従業員を削除できません」と伝えることができた場合、ユーザーは最初にチーム T1 に移動し、ユーザーを削除してからもう一度削除を試みることを知っています。これは単純な例です。先ほど述べたように、従業員は多くの関係に関与する可能性があるためです。私のアプリでは、少なくとも 20 の関係があります。

解決策は、SqlException によって報告されたメッセージを表示することですが、それはまったくエレガントではありません。まず、そのメッセージは非常に技術的です。FK、PK、トリガーについて述べていますが、これらはユーザーにとって無意味であり、ユーザーを怖がらせます。次に、私のアプリは多言語 UI を使用しており、すべてのメニューとメッセージはユーザーが選択した言語 (ログイン時またはユーザー プロファイルで選択) で表示されます。また、SqlException からのメッセージは英語 (英語版を使用している場合) であるか、SQL サーバーがその言語である場合、ドイツ語やオランダ語などの最悪のあまり一般的ではない言語です。

ユーザーに意味のあるメッセージを表示できるように、SQL 例外から意味のある情報を抽出するための一般的または推奨されるアプローチはありますか (たとえば、失敗の原因となったリレーションまたは子テーブル、またはトリガーなど)。しかし、言語に依存しない方法でプログラムでテストし、ユーザーフレンドリーな方法で独自のエラーメッセージをフォーマットできるものはありますか?

この状況をどのように処理しますか?

すべての回答に感謝します

(PS:長文すみません)

4

9 に答える 9

6

残念ながら、ここには簡単な答えはありません。

関連する作業量は、ビジネス層からのエラー メッセージの一貫性によって異なります。「技術的な」エラー メッセージをユーザー向けのメッセージに変換する必要があります。

これは、エラー メッセージから、言語固有のエラー メッセージを引き出すために使用できるリソース キーへの何らかの形式のルックアップを作成することの問題です。ただし、メッセージを解析して詳細情報 (テーブル名など) を取得する必要がある場合は、少し複雑になります。その場合、おそらく、エラー メッセージを何らかの形式の正規表現/プロセッサと新しいリソース文字列にマップするものが必要になるでしょう。次に、元のエラーから抽出した情報を使用してユーザーの文字列をフォーマットし、それをユーザーに提示できます。

于 2009-09-12T13:15:23.303 に答える
5

データベースからは、「外部キー関係 FK_something_to_another の違反」などの技術的なメッセージしか表示されません。

通常、SqlException では、SQL エラー コードなども取得します。

最良のアプローチは、データベースに別のテーブルを用意し、発生する可能性のある技術的な SQL エラーを、より意味のあるユーザー指向のメッセージに基本的にマップすることです。たとえば、SQL エラーが「fk violation blablabaal」のような内容を示している場合、「UserErrorTable」にエントリがあり、これを「ユーザー (this.and.that) を削除できませんでした。おそらく .. ...(彼はまだチームのメンバーです)」など。

次に、ビジネス層でこれらの SqlExceptions をキャッチし、それらの技術情報をユーザー向けのカスタム例外に変換し、ユーザー フレンドリーなメッセージを挿入して、技術的な例外をカスタム例外タイプの .InnerException に貼り付けます。

public class UserFriendlyException : Exception
{
  public string UserErrorMessage { get; set; }

  public UserFriendlyException(string message, SqlException exc) : base(message, exc)
  {
     UserErrorMessage = MapTechnicalExecptionToUserMessage(exc);
  }
}

マルク

于 2009-09-12T13:17:32.980 に答える
3

簡単な答えは「しない」です。エラーがグローバル エラー処理/ログにバブル アウトするようにします。検証とビジネス ルールでは通常、データベースの例外を排除する必要があるため、ハードかつ迅速に失敗し、ダーティ データを送信しない方がよいでしょう。トランザクションも役立ちます。

于 2009-09-12T15:21:50.787 に答える
2

これを行う方法は、ストアド プロシージャを記述し、 and を使用することTRYですCATCH。を使用RAISERRORして、独自のカスタム メッセージを表示し、 でエラー コードを確認しますSqlException

于 2009-09-12T13:15:36.953 に答える
2

エラー メッセージは例外と同一視しません。エラー メッセージは、ユーザーが情報を提供し、最も重要なアクションを起こす必要があるものです。ユーザー エクスペリエンス ガイドラインには、エラー メッセージに関するガイドラインがいくつかあります。一読することをお勧めします。Apple は、 Writing Good Alert Messagesに優れた一般的なガイドラインもあります。

すべてではないにしてもほとんどの SQL エラーは、適切なエンド ユーザー メッセージではないことがすぐにわかります。「制約 FKXF#455 違反」 - エンド ユーザーにとっては悪いエラーです。「ファイル グループがいっぱいです」 - エンド ユーザーにとっては悪いエラーです。「デッドロック」 - 同じ。優れたアプリケーションが行うことは、ユーザーの役割を分離することです。エンド ユーザーではなく、管理者がこれらのエラーを確認する必要があります。そのため、アプリケーションは常に完全な SQL エラーをすべての詳細とともにログに記録し、最終的に管理者に通知してから、「システム エラーが発生しました。管理者に通知されました」などの別のエラーをユーザーに表示します。

エンド ユーザーが SQL エラーに対処できる場合は、エラー メッセージを表示して、問題を修正する方法をユーザーに指示できます (たとえば、制約を満たすために入力の請求書の日付を変更するなど)。しかし、この場合でも、ほとんどの場合、SQL エラーをユーザーに直接表示するべきではありません (表示しない理由は非常によくわかりました: ローカリゼーション)。これにより、開発チームの作業負荷が大幅に増大することは承知しています。キャッチできる多数のエラーの中から、それぞれの場合にユーザーが対処できるエラーを把握する必要があります。これはよく知られていることであり、優れたプログラム マネージャーが、コードの約 80% がエラー ケースを処理していること、およびアプリケーションが「完了」とは通常 20% が完了したことを意味することを知っている理由です。これが、優れたアプリと通常のアプリの違いです。.

私の提案は、段階的開示の原則から始めることです。「操作が失敗しました」という一般的なエラー メッセージを表示します。ユーザーがエラー メッセージ ダイアログの [詳細を表示...] ボタンを押した場合に詳細を表示し、SqlError コレクションを表示します (ところで、SqlExceptionではなくSqlException.Errorsの SqlError コレクション全体を常にログに記録して表示する必要があります)。 )。SqlError.Numberプロパティを使用して、catch ブロックにロジックを追加し、ユーザーがエラーについて何かできるかどうか (エラーが対処可能かどうかを決定する) を決定し、適切な情報を追加します。

残念ながらピクシーダストはありません。おそらくプロジェクトの最も難しい部分の足元に触れています。

于 2009-09-13T20:43:28.977 に答える
1

私たちは通常、プロジェクトで何らかのトランスレーターを作成します。SQL 例外メッセージをいくつかの事前定義されたパターンと比較し、同等のメッセージをユーザーに表示します。

于 2009-09-12T13:18:19.970 に答える
1

一般的な技術>ユーザーフレンドリーなエラーについては、すでに提供されている回答のみをサポートできます。

しかし、雇用主との具体的な例では、SqlException だけに頼らないように勧めなければなりません。従業員を削除しようとする前に、従業員がチームの一員であるか、マネージャーであるかなどを確認する必要があります。これにより、アプリケーションの使いやすさが大幅に向上します。

擬似:

Employee e;
try {

   IEnumerable<Team> teams = Team.GetTeamsByEmployee(e);
   if (teams.Count() > 0) {
       throw new Exception("Employee is a part of team "+ String.Join(",", teams.Select(o => o.Name).ToArray());
   }

   IEnumerable<Employee> managingEmployees = Employee.GetEmployeesImManaging(e);
   if (managingEmployees.Count() > 0) {
       throw new Exception("Employee is manager for the following employees "+ String.Join(",", managingEmployees.Select(o => o.Name).ToArray());
   }

   Employee.Delete(e);
} catch (Exception e) {
   // write out e
}
于 2009-09-12T14:07:31.293 に答える
1

エラーが発生します。発生したエラーの種類や種類、またはその処理方法が特に重要でない場合は、コードを TRY...CATCH... ブロックで平手打ちし、一般的なエラー報告システムを記述しても問題ありません。それよりも優れたものを書きたい (または要求されている) 場合は、いくつかの以前の投稿 (私も賛成しました) で概説されているように、かなりの労力が必要になる可能性があります。

私はエラーを予測可能または予測不可能に分類する傾向があります。エラーが予測でき、「従業員の削除」状況など、明確で適切なメッセージでエラー処理したい場合は、それに応じて計画とコーディングを行う必要があります。その定義では、予期しないエラーに対してこれを行うことはできません。通常、TRY/CATCH の出番です。

あなたの状況では、行を削除する前に、子テーブルに対するクエリを介して、削除が成功するかどうかを確認する方法があります。そうでない場合は、その理由を正確に把握し (削除をブロックする最初のテーブルだけでなく、すべての子テーブルについて)、明確なメッセージをユーザーに提示できます。残念ながら、チェックと実際の削除の間でデータが変更される可能性があるかどうかを考慮する必要があります。ここではおそらく問題ではありませんが、他の状況では問題になる可能性があります。

TRY...CATCH... に基づく別の方法は、catch ブロック内のエラー番号をチェックすることです。「外部キーが原因で削除できない」場合は、子テーブルにクエリを実行してメッセージを生成できます。その他の予期しないエラーの場合は、一般的なメッセージにフォールバックする必要があります。

(注意: エラーが発生した場合、SQL は 2 つのエラー メッセージを続けて表示します [FK 制約違反はそのうちの 1 つですか?]。このような状況では、さまざまな ERROR() 関数は 2 番目とこれは信じられないほど腹立たしいことですが、それに対してできることはあまりありません。)

于 2009-09-12T14:42:54.517 に答える