Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Microsoft. Data.SqlClient melaporkan kegagalan server dan penyedia melalui SqlException. Operasi basis data dapat mengembalikan beberapa kesalahan, jadi periksa koleksi milik pengecualian Errors daripada hanya mengandalkan nilai gabungan Message.
Peringatan dan pesan informasi dari mesin basis data dikirim melalui InfoMessage. Lakukan subscribe sebelum membuka koneksi atau menjalankan perintah yang dapat menghasilkan pesan.
Tangani kegagalan basis data dalam urutan berikut:
- Tangkap
SqlExceptionpada boundary yang dapat menambahkan konteks operasi atau memilih aksi pemulihan. - Periksa setiap
SqlErrordalam koleksiErrors. - Rekam bidang diagnostik terstruktur tanpa mencatat kredensial atau nilai sensitif.
- Periksa status koneksi setelah kegagalan terkait koneksi.
- Coba ulang hanya ketika kesalahan bersifat sementara dan operasi aman untuk diulang.
Contoh dalam artikel ini mengasumsikan bahwa sebuah koneksi terbuka SqlConnection bernama connection menargetkan database contoh AdventureWorks. Contoh C# memerlukan using Microsoft.Data.SqlClient;.
Terima pesan informatif
event InfoMessage menerima pesan server dengan tingkat keparahan rendah, termasuk output dari PRINT. Satu event dapat berisi beberapa SqlError objek. Berlangganan sebelum menjalankan perintah yang dapat menghasilkan pesan:
connection.InfoMessage += (_, eventArgs) =>
{
foreach (SqlError message in eventArgs.Errors)
{
Console.WriteLine(
$"Info {message.Number}, class {message.Class}: {message.Message}");
}
};
using var command = new SqlCommand(
"PRINT N'Connected and ready.';",
connection);
command.ExecuteNonQuery();
Cuplikan tersebut mencetak:
Info 0, class 0: Connected and ready.
Tangkap SqlException
Tangkap SqlException di sekitar operasi database yang bisa Anda tangani atau tambahkan konteks. Jangan gunakan broad catch untuk mengubah operasi database yang gagal menjadi hasil yang sukses.
Operasi basis data dapat mengembalikan lebih dari satu kesalahan. Setiap SqlError dalam Errors mencakup bidang terstruktur:
| Property | Meaning |
|---|---|
Number |
Nomor kesalahan mesin database atau provider. |
Class |
Keparahan. |
State |
Status yang didefinisikan server yang dapat membedakan lokasi untuk nomor kesalahan yang sama. |
LineNumber |
Nomor baris yang dilaporkan. |
Procedure |
Nama prosedur atau pemicu jika disediakan. |
Server |
Nama server yang dilaporkan untuk kesalahan tersebut. |
Message |
Pesan kesalahan yang mudah dibaca pengguna. |
Properti kemudahan pada SqlException, termasuk Number, Class, State, LineNumber, Procedure, dan Server, hanya mencerminkan kesalahan pertama. Kutipan berikut mencetak pesan lalu memunculkan error dalam batch yang sama. Aplikasi ini membaca exception.Number dan kemudian melakukan iterasi Errors untuk melaporkan setiap entri:
using (var failing = new SqlCommand("""
PRINT N'Starting controlled operation.';
RAISERROR (N'The controlled operation failed.', 16, 1);
""", connection))
{
try
{
await failing.ExecuteNonQueryAsync();
}
catch (SqlException exception)
{
Console.WriteLine($"First error number: {exception.Number}");
foreach (SqlError error in exception.Errors)
{
Console.WriteLine(
$"Number={error.Number}; Class={error.Class}; " +
$"State={error.State}; Message={error.Message}");
}
}
}
Cuplikan tersebut mencetak:
First error number: 50000
Number=50000; Class=16; State=1; Message=The controlled operation failed.
Number=0; Class=0; State=1; Message=Starting controlled operation.
Outputnya PRINT tidak memunculkan InfoMessage. Ketika sebuah batch mengembalikan kesalahan yang memunculkan SqlException, penyedia menambahkan pesan informasi batch tersebut ke akhir SqlException.Errors.
Kutipan ini menangani kesalahan terkontrol hanya untuk menampilkan bidangnya. Dalam kode aplikasi, lempar ulang dengan throw; kecuali blok tangkap memilih aksi pemulihan yang nyata. Ambil keputusan berdasarkan nomor kesalahan yang terdokumentasi dan konteks operasi. Jangan mengurai teks pesan yang dilokalisasi.
Periksa status koneksi
Mesin basis data menetapkan kelas tingkat keparahan untuk setiap pesan. Kelas turunan bersifat informatif. Kelas yang diperlakukan sebagai kesalahan menghasilkan SqlException. Kesalahan tingkat keparahan tinggi dapat membuat koneksi tertutup atau rusak.
Setelah terjadi kesalahan, periksa State sebelum menggunakan kembali koneksi:
using var command = new SqlCommand(
"RAISERROR (N'Controlled failure.', 16, 1);",
connection);
try
{
command.ExecuteNonQuery();
}
catch (SqlException)
{
Console.WriteLine($"Connection state: {connection.State}");
}
Jangan berasumsi koneksi tetap bisa digunakan hanya karena pengecualian tertangkap. Kesalahan tingkat keparahan 16 biasanya membuatnya terbuka, tetapi kegagalan yang memutus koneksi dapat menutupnya. Lepaskan koneksi yang sudah ditutup atau rusak dan dapatkan koneksi lain dari pool koneksi. Jangan encode kebijakan coba ulang hanya dari tingkat keparahan saja. Operasi juga harus aman untuk diulang.
Identifikasi perintah batch yang gagal
Ketika SqlBatch gagal, SqlException.BatchCommand dapat merujuk pada SqlBatchCommand yang terkait. Properti ini adalah null jika penyedia tidak dapat mengaitkan kegagalan ke satu perintah batch tertentu. Kutipan berikut menjalankan dua perintah batch. Yang kedua gagal:
using (var batch = new SqlBatch(connection))
{
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1;"));
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1 / 0;"));
try
{
await batch.ExecuteNonQueryAsync();
}
catch (SqlException exception)
{
Console.WriteLine($"Error {exception.Number}: {exception.Message}");
if (exception.BatchCommand is SqlBatchCommand failedCommand)
{
Console.WriteLine($"Failed command: {failedCommand.CommandText}");
}
}
}
Cuplikan tersebut menampilkan:
Error 8134: Divide by zero error encountered.
Failed command: SELECT 1 / 0;
Teks perintah dapat mengandung literal sensitif dalam aplikasi yang diparameterisasi dengan buruk. Utamakan nama operasi aplikasi yang stabil dalam log produksi, dan jangan mencatat nilai parameter secara default.
Arahkan kesalahan pengguna ke InfoMessage
FireInfoMessageEventOnUserErrors defaultnya adalah false. Ketika Anda mengaturnya ke true, kesalahan dengan tingkat keparahan 16 atau lebih rendah akan masuk ke handler InfoMessage, bukan memunculkan SqlException:
connection.InfoMessage += (_, eventArgs) =>
{
foreach (SqlError message in eventArgs.Errors)
{
Console.WriteLine(
$"Info {message.Number}, class {message.Class}: {message.Message}");
}
};
connection.FireInfoMessageEventOnUserErrors = true;
using var command = new SqlCommand(
"RAISERROR (N'Routed to InfoMessage.', 16, 1);",
connection);
command.ExecuteNonQuery();
Tidak ada pengecualian yang dilemparkan. Cuplikan tersebut mencetak:
Info 50000, class 16: Routed to InfoMessage.
Gunakan opsi ini hanya ketika aplikasi memiliki model kesalahan berbasis peristiwa yang disengaja, karena kode pemanggil tidak lagi melihat pengecualian. Kesalahan dengan tingkat keparahan 17 atau lebih tinggi tetap memunculkan SqlException. Atur properti sekali sebagai bagian dari konfigurasi koneksi daripada mengaktifkan/menonaktifkannya di sekitar perintah individual.
Konteks diagnostik log
Tangkap konteks terstruktur yang cukup untuk mengkorelasikan kegagalan tanpa mengekspos data. Kutipan berikut mencatat nama operasi aplikasi, ClientConnectionId, dan field dari setiap SqlError.
ClientConnectionId membantu menghubungkan kegagalan klien dengan diagnostik server dan jaringan. Itu bukan kredensial.
const string operation = "LoadOrders";
using (var load = new SqlCommand("SELECT 1 / 0;", connection))
{
try
{
await load.ExecuteScalarAsync();
}
catch (SqlException exception)
{
Console.WriteLine(
$"Operation={operation}; " +
$"ClientConnectionId={exception.ClientConnectionId}");
foreach (SqlError error in exception.Errors)
{
Console.WriteLine(
$"Number={error.Number}; Class={error.Class}; " +
$"State={error.State}; Procedure={error.Procedure}; " +
$"Line={error.LineNumber}");
}
}
}
Nilai ClientConnectionId berbeda pada setiap koneksi. Cuplikan ini mencetak output yang mirip dengan:
Operation=LoadOrders; ClientConnectionId=<connection ID>
Number=8134; Class=16; State=1; Procedure=; Line=1
Catat juga apakah transaksi aktif dan, jika BatchCommand tersedia, perintah batch mana yang gagal. Sertakan pengenal server dan basis data yang sudah disterilkan hanya jika kebijakan organisasi mengizinkannya. Jangan mencatat string koneksi, token akses, kata sandi, atau nilai parameter sensitif. Gunakan penghitung diagnostik, pelacakan sumber event, dan panduan pemecahan masalah SqlClient untuk investigasi lebih mendalam.
Konten terkait
- Kembali ke alur kerja eksekusi perintah.
- Untuk mengidentifikasi perintah yang gagal dalam grup, jalankan beberapa perintah dengan SqlBatch.
- Untuk menambahkan pengulangan terbatas, gunakan logika retry yang dapat dikonfigurasi.
- Jika kolom pengecualian tidak cukup, gunakan panduan pemecahan masalah SqlClient.