1つのSqlException
(可能性があります)複数のSQLServerエラーをラップします。 Errors
でそれらを繰り返すことができます 財産。各エラーはSqlError
です :
foreach (SqlError error in exception.Errors)
各SqlError
Class
があります 再試行できるかどうかを大まかに判断するために使用できるプロパティ(接続を再作成する必要がある場合も再試行する場合)。 MSDNから:
Class
<10は、渡した情報のエラーの場合です。最初に入力を修正しないと、(おそらく)再試行できません。Class
11から16までは「ユーザーによって生成された」ものであり、ユーザーが最初に入力を修正しないと、おそらく何もできません。クラス16には多くの一時的なが含まれていることに注意してください エラーとクラス13はデッドロック用です(EvZのおかげで)。したがって、これらのクラスを1つずつ処理する場合は、これらのクラスを除外できます。Class
17から24までは一般的なハードウェア/ソフトウェアエラーであり、再試行できます。Class
の場合 20以上の場合、接続を再作成する必要があります それも。 22と23は重大なハードウェア/ソフトウェアエラーである可能性があり、24はメディアエラーを示します(ユーザーに警告する必要がありますが、単なる「一時的な」エラーの場合は再試行できます)。
各クラスの詳細については、こちらをご覧ください。
一般に、クラスでエラーを処理する場合、各エラーを正確に知る必要はありません(error.Number
を使用) プロパティまたはexception.Number
これは最初のSqlError
の単なるショートカットです そのリストで)。これには、役に立たない場合(またはエラーを回復できない場合)に再試行できるという欠点があります。 2ステップのアプローチをお勧めします :
- 既知のエラーコードを確認します(
SELECT * FROM master.sys.messages
でエラーコードを一覧表示します )処理したいものを確認します(方法を知っています)。このビューには、サポートされているすべての言語のメッセージが含まれているため、msglangid
でフィルタリングする必要がある場合があります 列(たとえば、英語の場合は1033)。 - 他のすべてはエラークラスに依存し、
Class
のときに再試行します 13以上が16以上です(20以上の場合は再接続します)。 - 重大度が21(22、23、および24)を超えるエラーは重大なエラーであり、待機してもその問題は修正されません(データベース自体も破損している可能性があります)。
上位クラスについて一言。これらのエラーの処理方法は単純ではなく、多くの要因(リスク管理を含む)によって異なります。 アプリケーション用)。簡単な最初のステップとして、書き込み操作を試みるときに22、23、および24を再試行しません。データベース、ファイルシステム、またはメディアが深刻な損傷を受けている場合、新しいデータを書き込むとデータの整合性がさらに低下する可能性があります(SQLServerは非常に注意が必要です。重大な状況でも、クエリのDBを危険にさらさないでください)。損傷したサーバーは、DBネットワークアーキテクチャによって異なりますが、ホットスワップされる可能性もあります(自動的に、指定された時間が経過した後、または指定されたトリガーが起動されたとき)。常にDBAに相談し、近くで作業してください。
再試行の戦略は、処理しているエラーによって異なります。リソースを解放する、保留中の操作が完了するのを待つ、別のアクションを実行するなどです。通常、すべての場合にのみ再試行する必要があります。 エラーは「再試行可能」です:
bool rebuildConnection = true; // First try connection must be open
for (int i=0; i < MaximumNumberOfRetries; ++i) {
try {
// (Re)Create connection to SQL Server
if (rebuildConnection) {
if (connection != null)
connection.Dispose();
// Create connection and open it...
}
// Perform your task
// No exceptions, task has been completed
break;
}
catch (SqlException e) {
if (e.Errors.Cast<SqlError>().All(x => CanRetry(x))) {
// What to do? Handle that here, also checking Number property.
// For Class < 20 you may simply Thread.Sleep(DelayOnError);
rebuildConnection = e.Errors
.Cast<SqlError>()
.Any(x => x.Class >= 20);
continue;
}
throw;
}
}
すべてをtry
でラップします /finally
接続を適切に破棄します。この単純な偽のナイーブなCanRetry()
機能:
private static readonly int[] RetriableClasses = { 13, 16, 17, 18, 19, 20, 21, 22, 24 };
private static bool CanRetry(SqlError error) {
// Use this switch if you want to handle only well-known errors,
// remove it if you want to always retry. A "blacklist" approach may
// also work: return false when you're sure you can't recover from one
// error and rely on Class for anything else.
switch (error.Number) {
// Handle well-known error codes,
}
// Handle unknown errors with severity 21 or less. 22 or more
// indicates a serious error that need to be manually fixed.
// 24 indicates media errors. They're serious errors (that should
// be also notified) but we may retry...
return RetriableClasses.Contains(error.Class); // LINQ...
}
ここで重大ではないエラーのリストを見つけるためのかなりトリッキーな方法。
通常、私はこのすべての(ボイラープレート)コードを1つのメソッドに埋め込みます(非表示 すべての汚いもの この署名を使用して接続を作成/破棄/再作成するために実行):
public static void Try(
Func<SqlConnection> connectionFactory,
Action<SqlCommand> performer);
このように使用するには:
Try(
() => new SqlConnection(connectionString),
cmd => {
cmd.CommandText = "SELECT * FROM master.sys.messages";
using (var reader = cmd.ExecuteReader()) {
// Do stuff
}
});
スケルトン(エラーで再試行)は、SQL Serverを使用していないときにも使用できることに注意してください(実際には、I / Oやネットワーク関連のものなど、他の多くの操作に使用できるため、一般的な関数を作成することをお勧めします広範囲に再利用します。