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.
Gunakan metode asinkron berbasis tugas untuk menghindari pemblokiran thread saat Microsoft.Data.SqlClient menunggu koneksi dan I/O jaringan. Eksekusi asinkron tidak membuat perintah atau pembaca aman untuk penggunaan secara bersamaan, dan tidak mengurangi waktu eksekusi query di server.
Contoh yang melakukan query tabel aplikasi menggunakan database contoh AdventureWorks. Contoh C# membutuhkan using Microsoft.Data.SqlClient;.
Konversi alur pemanggilan menjadi kode asinkron
Konversi satu jalur panggilan lengkap pada satu waktu:
- Buat metode induk menjadi asinkron.
- Ganti panggilan koneksi, perintah, reader, dan transaksi dengan padanan berbasis tugasnya.
-
awaitselesaikan setiap operasi sebelum menggunakan objek yang sama lagi. - Teruskan token pembatalan penelepon melalui setiap operasi yang dapat dibatalkan oleh penelpon.
- Tetapkan batas waktu perintah yang terbatas sebagai batas operasi database.
Gunakan await sejak pembukaan koneksi hingga pemrosesan hasil. Kutipan berikut mengasumsikan bahwa connectionString dan sql sudah didefinisikan. Ini membuat CancellationTokenSource dengan batas waktu terbatas untuk operasi:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellation.Token);
using var command = new SqlCommand(sql, connection);
await using SqlDataReader reader =
await command.ExecuteReaderAsync(cancellation.Token);
while (await reader.ReadAsync(cancellation.Token))
{
Console.WriteLine(reader.GetValue(0));
}
ExecuteReaderAsync mengembalikan saat hasil pertama tersedia, bukan setelah semua baris tiba. Setiap pemanggilan ReadAsync memajukan satu baris, sehingga loop memproses baris saat tiba dan membebaskan thread hanya saat menunggu data tambahan dari server. Token 30 detik membatasi seluruh operasi, termasuk loop baca.
Hanya satu eksekusi asinkron yang dapat tertunda pada SqlCommand. Hanya satu operasi bacaan asinkron, transisi hasil asinkron, atau operasi field asinkron yang dapat tertunda pada sebuah SqlDataReader. Tunggu setiap operasi sebelum memulai operasi berikutnya pada objek yang sama.
Fitur pemrograman asinkron
Metode sinkron berikut memiliki padanan berbasis tugas yang menerima token pembatalan:
| Metode sinkron | Metode asinkron | Contoh |
|---|---|---|
SqlConnection.Open |
SqlConnection.OpenAsync |
Konversi metode sinkron |
SqlCommand.ExecuteNonQuery |
SqlCommand.ExecuteNonQueryAsync |
Jalankan perintah secara asinkron |
SqlCommand.ExecuteScalar |
SqlCommand.ExecuteScalarAsync |
Jalankan perintah secara asinkron |
SqlCommand.ExecuteXmlReader |
SqlCommand.ExecuteXmlReaderAsync |
Jalankan perintah secara asinkron |
SqlCommand.ExecuteReader |
SqlCommand.ExecuteReaderAsync |
Ubah alur panggilan menjadi kode asinkron |
SqlDataReader.Read |
SqlDataReader.ReadAsync |
Konversi alur panggilan menjadi kode asinkron |
SqlDataReader.NextResult |
SqlDataReader.NextResultAsync |
Baca hasil secara asinkron |
SqlDataReader.IsDBNull |
SqlDataReader.IsDBNullAsync |
Baca hasil secara asinkron |
SqlDataReader.GetFieldValue<T> |
SqlDataReader.GetFieldValueAsync<T> |
Baca hasil secara asinkron |
SqlBatch.ExecuteNonQuery, ExecuteScalar, dan ExecuteReader |
SqlBatch.ExecuteNonQueryAsync, ExecuteScalarAsync, dan ExecuteReaderAsync |
Jalankan batch |
SqlBulkCopy.WriteToServer |
SqlBulkCopy.WriteToServerAsync |
Gunakan operasi asinkron dengan SqlBulkCopy |
Gunakan pembuangan sinkron ketika sebuah objek hanya mengimplementasikan IDisposable. Gunakan await using untuk objek yang mengimplementasikan IAsyncDisposable. Buang reader sebelum menggunakan kembali koneksinya.
Jalankan perintah secara asinkron
Contoh berikut mengasumsikan bahwa connection itu terbuka. Gunakan ExecuteScalarAsync untuk satu nilai, dan berikan token pembatalan milik penelepon:
using var cancellation = new CancellationTokenSource(
TimeSpan.FromSeconds(30));
CancellationToken token = cancellation.Token;
using var count = new SqlCommand(
"SELECT COUNT(*) FROM Production.Product;",
connection);
int total = (int)(await count.ExecuteScalarAsync(token))!;
Console.WriteLine($"Row count: {total}");
Gunakan ExecuteNonQueryAsync untuk pernyataan yang mengubah data. Gunakan ExecuteXmlReaderAsync untuk sebuah FOR XML kueri. Lepaskan pembaca XML sebelum Anda menjalankan perintah lain pada koneksi yang sama. Tanpa Multiple Active Result Sets (MARS), sebuah koneksi hanya mengizinkan satu reader terbuka.
Baca hasil secara asinkron
Contoh berikut meneruskan token pembatalan yang sama ke masing-masing panggilan asinkron ini:
-
ExecuteReaderAsyncmenjalankan kueri. -
ReadAsyncmembaca setiap baris. -
GetFieldValueAsyncmembaca nilai setiap kolom. -
NextResultAsyncbergerak ke set hasil berikutnya. -
IsDBNullAsyncmemeriksaNULL.
using var cancellation = new CancellationTokenSource(
TimeSpan.FromSeconds(30));
CancellationToken token = cancellation.Token;
using var query = new SqlCommand("""
SELECT TOP (3) ProductID, Name
FROM Production.Product
ORDER BY ProductID;
SELECT TOP (1) ProductID, Color
FROM Production.Product
WHERE Color IS NULL;
""", connection);
await using SqlDataReader reader = await query.ExecuteReaderAsync(token);
while (await reader.ReadAsync(token))
{
int id = await reader.GetFieldValueAsync<int>(0, token);
string name = await reader.GetFieldValueAsync<string>(1, token);
Console.WriteLine($"{id}: {name}");
}
if (await reader.NextResultAsync(token) && await reader.ReadAsync(token))
{
int productId = await reader.GetFieldValueAsync<int>(0, token);
bool colorIsNull = await reader.IsDBNullAsync(1, token);
Console.WriteLine($"{productId}: Color is NULL: {colorIsNull}");
}
NextResultAsync bergerak ke set hasil berikutnya.
GetFieldValueAsync dan IsDBNullAsync bacalah satu kolom dari baris saat ini. Saat Anda menggunakan default CommandBehavior, baris sudah di-buffer, jadi panggilan ini biasanya selesai tanpa menunggu. Saat Anda menggunakan CommandBehavior.SequentialAccess, proses dapat menunggu data kolom dari jaringan. Untuk informasi lebih lanjut tentang akses berurutan, lihat dukungan streaming SqlClient.
Konversi metode sinkron
Metode sinkron ini membuka koneksi, menjalankan perintah, dan mengembalikan jumlah baris yang terpengaruh dalam sys.objects:
static int CountObjects(string connectionString)
{
using var connection = new SqlConnection(connectionString);
connection.Open();
using var command = new SqlCommand("SELECT COUNT(*) FROM sys.objects;", connection);
return (int)command.ExecuteScalar()!;
}
Untuk mengonversi metode ke kode asinkron, lakukan perubahan berikut:
- Ubah tipe pengembalian menjadi
Task<int>. - Tambahkan parameter
CancellationToken. - Ganti
OpendenganOpenAsync, dan gantiExecuteScalardenganExecuteScalarAsync. - Lepaskan koneksi dengan
await using.
Metode yang dikonversi terlihat seperti ini:
static async Task<int> CountObjectsAsync(
string connectionString,
CancellationToken cancellationToken)
{
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);
using var command = new SqlCommand("SELECT COUNT(*) FROM sys.objects;", connection);
return (int)(await command.ExecuteScalarAsync(cancellationToken))!;
}
Pemanggil membuat token dan menunggu hasilnya:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
int objectCount = await CountObjectsAsync(connectionString, cancellation.Token);
Console.WriteLine($"Objects: {objectCount}");
Kedua metode mengembalikan jumlah yang sama. Output tergantung pada database yang Anda hubungkan ke.
Connect Timeout masih membatasi pembuatan koneksi dan waktu tunggu pool. Membatalkan token dapat mengembalikan kendali ke pemanggil lebih cepat.
Tambahkan panggilan asinkron ke aplikasi yang sudah ada
Konversi satu jalur panggilan lengkap dalam satu waktu. Hindari memanggil .Result, .Wait(), atau GetAwaiter().GetResult() pada tugas database karena panggilan tersebut memblokir dan dapat melakukan deadlock di lingkungan dengan konteks sinkronisasi.
Jangan memulai perintah asinkron lalu menggunakan perintah atau reader yang sama secara sinkron sebelum tugas selesai. Tanpa MARS, jangan gunakan koneksi yang sama secara sinkron sebelum tugas selesai.
Tambahkan pembatalan dan batas waktu
Membatalkan operasi asinkron
Teruskan token pembatalan yang sama dari pembukaan koneksi hingga eksekusi perintah:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellation.Token);
using var command = new SqlCommand(
"SELECT COUNT(*) FROM Production.Product;",
connection);
await command.ExecuteScalarAsync(cancellation.Token);
Pembatalan diupayakan semaksimal mungkin. Operasi dapat selesai sebelum pembatalan berlaku, dan pengecualian yang diamati bergantung pada saat pembatalan terjadi. Jangan berasumsi bahwa menerima permintaan pembatalan berarti server melakukan rollback pada pekerjaan yang sudah selesai. Ketika serangkaian perubahan harus berhasil atau gagal sebagai satu unit, gunakan transaksi eksplisit. Untuk informasi lebih lanjut, lihat Transaksi lokal.
Cancel meminta pembatalan perintah saat ini. Ini tidak menggantikan token pembatalan milik penelepon. Operasi dapat selesai sebelum permintaan pembatalan berlaku.
Setelah pembatalan atau timeout, tunggu operasi dan lepaskan reader serta command. Periksa status koneksi sebelum digunakan kembali. Kegagalan yang memutus koneksi memerlukan koneksi baru.
Mengonfigurasi batas waktu
Timeout koneksi dan perintah mencakup berbagai fase:
| Setting | Mencakup |
|---|---|
Connect Timeout |
Pembentukan koneksi dan menunggu koneksi pool. |
CommandTimeout |
Eksekusi perintah dan waktu tunggu pembacaan jaringan kumulatif. |
| Token pembatalan | Permintaan yang dikendalikan oleh penelepon untuk berhenti menunggu. |
CommandTimeout diukur dalam hitungan detik.
0 Artinya tidak ada batas waktu perintah. Nilai default adalah nilai koneksi Command Timeout jika dikonfigurasi, jika tidak, 30 detik.
Untuk pembaca, timeout perintah berlaku untuk waktu tunggu jaringan kumulatif untuk setiap operasi baca penyedia. Itu tidak termasuk waktu yang dihabiskan untuk memproses baris dalam kode aplikasi. Timeout dapat terjadi sebelum baris pertama atau saat mengambil baris berikutnya.
Gabungkan pembatalan dan timeout perintah
Gunakan timeout perintah terbatas dan pembatalan pemanggil secara bersamaan. Contoh ini mengasumsikan bahwa connection terbuka:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
using var command = new SqlCommand("""
SELECT TOP (10) SalesOrderID, OrderDate
FROM Sales.SalesOrderHeader
ORDER BY SalesOrderID;
""", connection)
{
CommandTimeout = 15
};
await using SqlDataReader reader =
await command.ExecuteReaderAsync(cancellation.Token);
while (await reader.ReadAsync(cancellation.Token))
{
Console.WriteLine($"{reader.GetInt32(0)} | {reader.GetDateTime(1):O}");
}
Jika penelepon membatalkan terlebih dahulu, panggilan yang ditunggu akan memunculkan pengecualian. Jenis pengecualian tergantung pada di bagian mana pembatalan terjadi. Jika timeout perintah berakhir terlebih dahulu, penyedia melaporkan timeout. Dalam kedua kasus, perintah mungkin berhasil selesai sebelum pembatalan atau timeout berlaku.
Pola asinkron tingkat lanjut
Gunakan metode asinkron melalui tipe penyedia dasar
Kode yang independen dari penyedia dapat menggunakan DbConnection, DbCommand, dan DbDataReader metode asinkron. Buat parameter melalui perintah provider dan hindari downcast kecuali aplikasi membutuhkan fitur khusus Microsoft.Data.SqlClient.
Gunakan metode transaksi SQL berbasis tugas
Gunakan metode transaksi lokal berbasis task saat kerangka kerja target mengekspos metode tersebut. Contoh berikut mengasumsikan bahwa connection terbuka, sql memegang teks perintah, dan pemanggil meneruskan cancellationToken:
await using SqlTransaction transaction =
(SqlTransaction)await connection.BeginTransactionAsync(cancellationToken);
using var command = new SqlCommand(sql, connection, transaction);
try
{
await command.ExecuteNonQueryAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken);
}
catch (Exception operationException)
{
try
{
await transaction.RollbackAsync(CancellationToken.None);
}
catch (Exception rollbackException)
{
throw new AggregateException(
"The operation and transaction rollback both failed.",
operationException,
rollbackException);
}
throw;
}
Gunakan token pembersihan terpisah untuk rollback. Token operasi mungkin sudah dibatalkan. Tangani kegagalan rollback tanpa membuang pengecualian operasi asli. Contoh ini mempertahankan kedua pengecualian dalam sebuah AggregateException. Sebagai gantinya, sebuah aplikasi dapat mencatat pengecualian rollback dan melempar kembali pengecualian asli.
Metode Microsoft.Data.SqlClient ini menggunakan implementasi turunan DbConnection dan DbTransaction, yang memanggil metode transaksi sinkron dan mengembalikan Task yang sudah selesai. Menunggu mereka tidak membuka blokir thread pemanggil selama operasi transaksi berjalan. Pembatalan diperiksa sebelum operasi dimulai dan tidak mengganggu operasi yang sedang berlangsung. Eksekusi perintah di dalam transaksi masih asinkron.
Gunakan transaksi terdistribusi asinkron
Perintah asinkron dapat dijalankan di dalam konteks TransactionScope. Cakupan ini menetapkan Transaction.Current, yang oleh .NET disebut sebagai transaksi ambient. Koneksi yang terbuka di dalam lingkup secara otomatis mendaftar dalam transaksi tersebut, karena kata kunci koneksi Enlist secara default adalah true. Konfigurasikan TransactionScopeAsyncFlowOption.Enabled saat transaksi harus mengalir melintasi await. Transaksi terdistribusi memiliki persyaratan platform, server, dan koordinator. Untuk informasi lebih lanjut, lihat integrasi System.Transactions.
Gunakan operasi asinkron dengan SqlBulkCopy
Gunakan WriteToServerAsync untuk penyalinan massal asinkron. Jaga pembaca sumber atau koleksi baris tetap hidup hingga tugas yang dikembalikan selesai. Konfigurasikan ukuran batch, perilaku transaksi, dan timeout untuk operasi. Untuk informasi lebih lanjut, lihat Operasi salinan massal.
Secara asinkron menggunakan beberapa perintah dengan MARS
MARS memungkinkan lebih dari satu perintah yang tertunda pada satu koneksi fisik. Aktifkan ini dengan MultipleActiveResultSets=True hanya ketika aplikasi membutuhkan model berbagi koneksi tersebut.
MARS tidak diperlukan untuk pemrograman asinkron. Tanpa MARS, selesaikan atau buang reader aktif sebelum menjalankan perintah lain pada koneksi yang sama. Koneksi kedua yang terpisah seringkali lebih baik untuk pekerjaan yang benar-benar mandiri.
Untuk perilaku sesi dan pembatasan transaksi, lihat Enable Multiple Active Result Sets.
Membaca dan memperbarui data secara asinkron dengan MARS
Saat Anda mengaktifkan MARS, satu perintah dapat membaca baris sementara perintah lain dieksekusi pada koneksi yang sama. Tunggu setiap operasi perintah dan pembacaan secara terpisah. Operasi MARS tidak aman untuk thread, dan eksekusi server masih bisa berserialisasi pada titik yield.
Ketika operasi baca dan pembaruan harus bersifat atomik, tetapkan transaksi lokal yang sama ke setiap perintah dan uji perilaku MARS transaksi tersebut. Jangan melakukan commit saat operasi lain dalam transaksi tersebut masih tertunda.
Pemrograman asinkron lama
Microsoft.Data.SqlClient mempertahankan metode BeginExecuteNonQuery, BeginExecuteReader, dan BeginExecuteXmlReader Asynchronous Programming Model (APM) untuk kompatibilitas. Jangan gunakan dalam kode baru.
CommandTimeout diabaikan oleh panggilan asinkron gaya lama ini dan dihormati oleh metode berbasis tugas seperti ExecuteReaderAsync.
Metode lama tidak memerlukan Asynchronous Processing=true. Microsoft. Data.SqlClient 4.0 dan versi yang lebih baru tidak mendukung Asynchronous Processing properti koneksi.
Konten terkait
- Mulailah dengan alur kerja eksekusi perintah sinkron.
- Untuk memproses baris, ambil data dengan SqlDataReader.
- Untuk mentransfer nilai besar, gunakan dukungan streaming SqlClient.
- Untuk perilaku sesi dan transaksi MARS, lihat Multiple Active Result Sets.