Satu SqlException
(mungkin) membungkus beberapa kesalahan SQL Server. Anda dapat mengulanginya dengan Errors
Properti. Setiap kesalahan adalah SqlError
:
foreach (SqlError error in exception.Errors)
Setiap SqlError
memiliki Class
properti yang dapat Anda gunakan untuk menentukan secara kasar apakah Anda dapat mencoba lagi atau tidak (dan jika Anda mencoba lagi jika Anda juga harus membuat ulang koneksi). Dari MSDN:
Class
<10 untuk kesalahan dalam informasi yang Anda berikan kemudian (mungkin) Anda tidak dapat mencoba lagi jika Anda tidak memperbaiki input terlebih dahulu.Class
dari 11 hingga 16 "dihasilkan oleh pengguna" maka mungkin sekali lagi Anda tidak dapat melakukan apa pun jika pengguna terlebih dahulu tidak memperbaiki inputnya. Harap dicatat bahwa kelas 16 mencakup banyak sementara kesalahan dan kelas 13 adalah untuk kebuntuan (terima kasih kepada EvZ) sehingga Anda dapat mengecualikan kelas ini jika Anda menanganinya satu per satu.Class
dari 17 hingga 24 adalah kesalahan perangkat keras/perangkat lunak umum dan Anda dapat mencoba lagi. KapanClass
adalah 20 atau lebih tinggi, Anda harus membuat ulang koneksi juga. 22 dan 23 mungkin merupakan kesalahan perangkat keras/lunak yang serius, 24 menunjukkan kesalahan media (sesuatu yang harus diperingatkan pengguna tetapi Anda dapat mencoba lagi jika itu hanya kesalahan "sementara").
Anda dapat menemukan deskripsi lebih rinci dari setiap kelas di sini.
Secara umum jika Anda menangani kesalahan dengan kelasnya, Anda tidak perlu tahu persis setiap kesalahan (menggunakan error.Number
properti atau exception.Number
yang hanya merupakan jalan pintas untuk SqlError
pertama dalam daftar itu). Ini memiliki kelemahan yang dapat Anda coba lagi saat tidak berguna (atau kesalahan tidak dapat dipulihkan). Saya akan menyarankan pendekatan dua langkah :
- Periksa kode kesalahan yang diketahui (daftar kode kesalahan dengan
SELECT * FROM master.sys.messages
) untuk melihat apa yang ingin Anda tangani (tahu caranya). Tampilan tersebut berisi pesan dalam semua bahasa yang didukung sehingga Anda mungkin perlu memfilternya denganmsglangid
kolom (misalnya 1033 untuk bahasa Inggris). - Untuk hal lain bergantung pada kelas kesalahan, coba lagi saat
Class
adalah 13 atau lebih tinggi dari 16 (dan menghubungkan kembali jika 20 atau lebih tinggi). - Kesalahan dengan tingkat keparahan lebih tinggi dari 21 (22, 23, dan 24) adalah kesalahan serius dan sedikit menunggu tidak akan memperbaiki masalah itu (basis data itu sendiri mungkin juga rusak).
Satu kata tentang kelas yang lebih tinggi. Cara menangani kesalahan ini tidak sederhana dan bergantung pada banyak faktor (termasuk manajemen risiko untuk aplikasi Anda). Sebagai langkah pertama yang sederhana, saya tidak akan mencoba lagi untuk 22, 23, dan 24 ketika mencoba operasi penulisan:jika database, sistem file, atau media rusak parah, maka penulisan data baru dapat memperburuk integritas data (SQL Server sangat berhati-hati untuk jangan kompromi DB untuk kueri bahkan dalam keadaan kritis). Server yang rusak, tergantung pada arsitektur jaringan DB Anda, bahkan mungkin hot-swapped (secara otomatis, setelah jangka waktu tertentu, atau saat pemicu tertentu diaktifkan). Selalu berkonsultasi dan bekerja sama dengan DBA Anda.
Strategi untuk mencoba kembali bergantung pada kesalahan yang Anda tangani:sumber daya gratis, menunggu operasi yang tertunda selesai, mengambil tindakan alternatif, dll. Secara umum Anda harus mencoba lagi hanya jika semua kesalahan "dapat dicoba lagi":
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;
}
}
Bungkus semuanya dalam try
/finally
untuk membuang sambungan dengan benar. Dengan CanRetry()
sederhana-palsu-naif ini fungsi:
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...
}
Beberapa cara yang cukup rumit untuk menemukan daftar kesalahan tidak kritis di sini.
Biasanya saya menyematkan semua kode (boilerplate) ini dalam satu metode (di mana saya dapat menyembunyikan semua hal-hal kotor selesai untuk membuat/membuang/membuat ulang koneksi) dengan tanda tangan ini:
public static void Try(
Func<SqlConnection> connectionFactory,
Action<SqlCommand> performer);
Untuk digunakan seperti ini:
Try(
() => new SqlConnection(connectionString),
cmd => {
cmd.CommandText = "SELECT * FROM master.sys.messages";
using (var reader = cmd.ExecuteReader()) {
// Do stuff
}
});
Harap dicatat bahwa kerangka (coba lagi pada kesalahan) dapat digunakan juga ketika Anda tidak bekerja dengan SQL Server (sebenarnya itu dapat digunakan untuk banyak operasi lain seperti I/O dan hal-hal terkait jaringan jadi saya sarankan untuk menulis fungsi umum dan menggunakannya kembali secara ekstensif).