API inti logika coba lagi yang dapat dikonfigurasi di SqlClient
Berlaku untuk: .NET Framework .NET .NET Standard
Jika penyedia logika coba lagi bawaan tidak memenuhi kebutuhan Anda, Anda dapat membuat penyedia kustom Anda sendiri. Anda kemudian dapat menetapkan penyedia tersebut ke objek SqlConnection
atau SqlCommand
untuk menerapkan logika kustom Anda.
Penyedia bawaan dirancang sekitar tiga antarmuka yang dapat digunakan untuk mengimplementasikan penyedia kustom. Penyedia coba lagi kustom kemudian dapat digunakan dengan cara yang sama seperti penyedia coba lagi internal pada SqlConnection atau SqlCommand:
- SqlRetryIntervalBaseEnumerator: Menghasilkan urutan interval waktu.
- SqlRetryLogicBase: Mengambil interval waktu berikutnya untuk enumerator tertentu, jika jumlah percobaan ulang belum terlampaui dan kondisi sementara terpenuhi.
- SqlRetryLogicBaseProvider: Menerapkan logika coba lagi ke operasi koneksi dan perintah.
Perhatian
Dengan menerapkan penyedia logika coba lagi kustom, Anda bertanggung jawab atas semua aspek, termasuk konkurensi, performa, dan manajemen pengecualian.
Contoh
Implementasi dalam sampel ini sesering mungkin untuk menunjukkan penyesuaian langkah demi langkah. Ini tidak termasuk praktik lanjutan seperti keamanan utas, asinkron, dan konkurensi. Untuk mendalami implementasi nyata, Anda dapat mempelajari logika coba lagi yang telah ditentukan sebelumnya di repositori GitHub Microsoft.Data.SqlClient.
Tentukan kelas logika coba lagi yang dapat dikonfigurasi kustom:
- Enumerator: Tentukan urutan interval waktu tetap dan perluas rentang waktu yang dapat diterima dari dua menit hingga empat menit.
public class CustomEnumerator : SqlRetryIntervalBaseEnumerator { // Set the maximum acceptable time to 4 minutes private readonly TimeSpan _maxValue = TimeSpan.FromMinutes(4); public CustomEnumerator(TimeSpan timeInterval, TimeSpan maxTime, TimeSpan minTime) : base(timeInterval, maxTime, minTime) {} // Return fixed time on each request protected override TimeSpan GetNextInterval() { return GapTimeInterval; } // Override the validate method with the new time range validation protected override void Validate(TimeSpan timeInterval, TimeSpan maxTimeInterval, TimeSpan minTimeInterval) { if (minTimeInterval < TimeSpan.Zero || minTimeInterval > _maxValue) { throw new ArgumentOutOfRangeException(nameof(minTimeInterval)); } if (maxTimeInterval < TimeSpan.Zero || maxTimeInterval > _maxValue) { throw new ArgumentOutOfRangeException(nameof(maxTimeInterval)); } if (timeInterval < TimeSpan.Zero || timeInterval > _maxValue) { throw new ArgumentOutOfRangeException(nameof(timeInterval)); } if (maxTimeInterval < minTimeInterval) { throw new ArgumentOutOfRangeException(nameof(minTimeInterval)); } } }
- Logika coba lagi: Terapkan logika coba lagi pada perintah apa pun yang bukan bagian dari transaksi aktif. Turunkan jumlah percobaan ulang dari 60 menjadi 20.
public class CustomRetryLogic : SqlRetryLogicBase { // Maximum number of attempts private const int maxAttempts = 20; public CustomRetryLogic(int numberOfTries, SqlRetryIntervalBaseEnumerator enumerator, Predicate<Exception> transientPredicate) { if (!(numberOfTries > 0 && numberOfTries <= maxAttempts)) { // 'numberOfTries' should be between 1 and 20. throw new ArgumentOutOfRangeException(nameof(numberOfTries)); } // Assign parameters to the relevant properties NumberOfTries = numberOfTries; RetryIntervalEnumerator = enumerator; TransientPredicate = transientPredicate; Current = 0; } // Prepare this object for the next round public override void Reset() { Current = 0; RetryIntervalEnumerator.Reset(); } public override bool TryNextInterval(out TimeSpan intervalTime) { intervalTime = TimeSpan.Zero; // First try has occurred before starting the retry process. // Check if retry is still allowed bool result = Current < NumberOfTries - 1; if (result) { // Increase the number of attempts Current++; // It's okay if the RetryIntervalEnumerator gets to the last value before we've reached our maximum number of attempts. // MoveNext() will simply leave the enumerator on the final interval value and we will repeat that for the final attempts. RetryIntervalEnumerator.MoveNext(); // Receive the current time from enumerator intervalTime = RetryIntervalEnumerator.Current; } return result; } }
- Penyedia: Menerapkan penyedia coba lagi yang mencoba kembali operasi sinkron tanpa
Retrying
peristiwa. TimeoutException Menambahkan ke nomor kesalahan pengecualian sementara yang adaSqlException.
public class CustomProvider : SqlRetryLogicBaseProvider { // Preserve the given retryLogic on creation public CustomProvider(SqlRetryLogicBase retryLogic) { RetryLogic = retryLogic; } public override TResult Execute<TResult>(object sender, Func<TResult> function) { // Create a list to save transient exceptions to report later if necessary IList<Exception> exceptions = new List<Exception>(); // Prepare it before reusing RetryLogic.Reset(); // Create an infinite loop to attempt the defined maximum number of tries do { try { // Try to invoke the function return function.Invoke(); } // Catch any type of exception for further investigation catch (Exception e) { // Ask the RetryLogic object if this exception is a transient error if (RetryLogic.TransientPredicate(e)) { // Add the exception to the list of exceptions we've retried on exceptions.Add(e); // Ask the RetryLogic for the next delay time before the next attempt to run the function if (RetryLogic.TryNextInterval(out TimeSpan gapTime)) { Console.WriteLine($"Wait for {gapTime} before next try"); // Wait before next attempt Thread.Sleep(gapTime); } else { // Number of attempts has exceeded the maximum number of tries throw new AggregateException("The number of retries has exceeded the maximum number of attempts.", exceptions); } } else { // If the exception wasn't a transient failure throw the original exception throw; } } } while (true); } public override Task<TResult> ExecuteAsync<TResult>(object sender, Func<Task<TResult>> function, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public override Task ExecuteAsync(object sender, Func<Task> function, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } }
Buat instans penyedia coba lagi yang terdiri dari jenis kustom yang ditentukan:
public static SqlRetryLogicBaseProvider CreateCustomProvider(SqlRetryLogicOption options) { // 1. create an enumerator instance CustomEnumerator customEnumerator = new CustomEnumerator(options.DeltaTime, options.MaxTimeInterval, options.MinTimeInterval); // 2. Use the enumerator object to create a new RetryLogic instance CustomRetryLogic customRetryLogic = new CustomRetryLogic(5, customEnumerator, (e) => TransientErrorsCondition(e, options.TransientErrors)); // 3. Create a provider using the RetryLogic object CustomProvider customProvider = new CustomProvider(customRetryLogic); return customProvider; }
- Fungsi berikut akan mengevaluasi pengecualian dengan menggunakan daftar pengecualian yang dapat diulang dan pengecualian khusus TimeoutException untuk menentukan apakah pengecualian tersebut dapat diulang:
// Return true if the exception is a transient fault. private static bool TransientErrorsCondition(Exception e, IEnumerable<int> retriableConditions) { bool result = false; // Assess only SqlExceptions if (retriableConditions != null && e is SqlException ex) { foreach (SqlError item in ex.Errors) { // Check each error number to see if it is a retriable error number if (retriableConditions.Contains(item.Number)) { result = true; break; } } } // Other types of exceptions can also be assessed else if (e is TimeoutException) { result = true; } return result; }
Gunakan logika coba lagi yang dikustomisasi:
- Tentukan parameter logika coba lagi:
// Define the retry logic parameters var options = new SqlRetryLogicOption() { // Tries 5 times before throwing an exception NumberOfTries = 5, // Preferred gap time to delay before retry DeltaTime = TimeSpan.FromSeconds(1), // Maximum gap time for each delay time before retry MaxTimeInterval = TimeSpan.FromSeconds(20), // SqlException retriable error numbers TransientErrors = new int[] { 4060, 1024, 1025} };
- Buat penyedia coba lagi kustom:
// Create a custom retry logic provider SqlRetryLogicBaseProvider provider = CustomRetry.CreateCustomProvider(options);
- Tetapkan penyedia coba lagi ke SqlConnection.RetryLogicProvider atau SqlCommand.RetryLogicProvider:
// Assumes that connection is a valid SqlConnection object // Set the retry logic provider on the connection instance connection.RetryLogicProvider = provider; // Establishing the connection will trigger retry if one of the given transient failure occurs. connection.Open();
Catatan
Jangan lupa untuk mengaktifkan sakelar logika coba lagi yang dapat dikonfigurasi sebelum menggunakannya. Untuk informasi selengkapnya, lihat Mengaktifkan logika coba lagi yang dapat dikonfigurasi.