SqlCommand.RetryLogicProvider Właściwość
Definicja
Ważne
Niektóre informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany przed wydaniem. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Pobiera lub ustawia wartość określającą SqlRetryLogicBaseProvider obiekt powiązany z tym poleceniem.
public:
property Microsoft::Data::SqlClient::SqlRetryLogicBaseProvider ^ RetryLogicProvider { Microsoft::Data::SqlClient::SqlRetryLogicBaseProvider ^ get(); void set(Microsoft::Data::SqlClient::SqlRetryLogicBaseProvider ^ value); };
[System.ComponentModel.Browsable(false)]
public Microsoft.Data.SqlClient.SqlRetryLogicBaseProvider RetryLogicProvider { get; set; }
public Microsoft.Data.SqlClient.SqlRetryLogicBaseProvider RetryLogicProvider { get; set; }
[<System.ComponentModel.Browsable(false)>]
member this.RetryLogicProvider : Microsoft.Data.SqlClient.SqlRetryLogicBaseProvider with get, set
member this.RetryLogicProvider : Microsoft.Data.SqlClient.SqlRetryLogicBaseProvider with get, set
Public Property RetryLogicProvider As SqlRetryLogicBaseProvider
Wartość właściwości
W przypadku ustawienia wartości null (ustawienie domyślne) zostanie użyty domyślny dostawca nienależący do pobierania.
- Atrybuty
Uwagi
Należy ustawić wartość dla tej właściwości przed wykonaniem polecenia, aby zaczęły obowiązywać.
Aby zastosować logikę ponawiania prób, przed wykonaniem polecenia wykonaj następujące czynności:
- Zdefiniuj parametry konfiguracji przy użyciu SqlRetryLogicOption typu.
- Utwórz obiekt SqlRetryLogicBaseProvider przy użyciu jednej z następujących metod statycznych SqlConfigurableRetryFactory klasy:
- SqlRetryLogicBaseProvider Przypisz obiekt do
RetryLogicProvider
właściwości .
Uwaga
Wykrywanie wyjątków, które można pobrać, jest istotną częścią wzorca ponawiania. Przed zastosowaniem logiki ponawiania należy zbadać wyjątki i wybrać dostawcę ponawiania, który najlepiej pasuje do danego scenariusza. Najpierw zarejestruj wyjątki i znajdź błędy przejściowe.
Uwaga
Limit czasu polecenia jest uruchamiany ponownie dla każdego wykonania polecenia w ramach logiki ponawiania i po zastosowaniu opóźnienia czasu ponawiania. Nie ma żadnego nakładania się czasu między tymi dwiema akcjami.
Uwaga
Domyślny dostawca logiki ponawiania prób nie jest włączony, chyba że jest skonfigurowany w pliku konfiguracji aplikacji. Aby uzyskać więcej informacji, zobacz Konfigurowanie pliku konfiguracji logiki ponawiania.
Przestroga
Polecenie z poleceniem CommandBehavior.CloseConnection nie jest zgodne z wbudowaną logiką ponawiania prób. Połączenie podstawowe jest natychmiast zamykane po pierwszej próbie wykonania i nie jest już dostępne dla kolejnych ponownych prób.
Przykład
Poniższy przykład tworzy bazę danych i nawiązuje z nią aktywne połączenie. Baza danych ma aktywne połączenie, ale próbuje usunąć ją przy użyciu nowego SqlConnection elementu i SqlCommand , który używa elementu SqlRetryLogicBaseProvider. Należy zabić aktywne połączenie za pośrednictwem bazy danych, aby odblokować drugie polecenie przed przekroczeniem liczby ponownych prób.
Połączenie blokujące symuluje sytuację, na przykład polecenie nadal działa w bazie danych i prawdopodobnie nie zostanie zakończone.
/// Detecting retriable exceptions is a vital part of the retry pattern.
/// Before applying retry logic it is important to investigate exceptions and choose a retry provider that best fits your scenario.
/// First, log your exceptions and find transient faults.
/// The purpose of this sample is to illustrate how to use this feature and the condition might not be realistic.
private const string DefaultDB = "Northwind";
private const string CnnStringFormat = "Server=localhost; Initial Catalog={0}; Integrated Security=true; pooling=false;";
private const string DropDatabaseFormat = "DROP DATABASE {0}";
private const string CreateDatabaseFormat = "CREATE DATABASE {0}";
// For general use
private static SqlConnection s_generalConnection = new SqlConnection(string.Format(CnnStringFormat, DefaultDB));
static void Main(string[] args)
{
// 1. Define the retry logic parameters
var options = new SqlRetryLogicOption()
{
NumberOfTries = 5,
MaxTimeInterval = TimeSpan.FromSeconds(20),
DeltaTime = TimeSpan.FromSeconds(1),
AuthorizedSqlCondition = null,
// error number 3702 : Cannot drop database "xxx" because it is currently in use.
TransientErrors = new int[] {3702}
};
// 2. Create a retry provider
var provider = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(options);
// define the retrying event to report execution attempts
provider.Retrying += (object s, SqlRetryingEventArgs e) =>
{
int attempts = e.RetryCount + 1;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"attempt {attempts} - current delay time:{e.Delay} \n");
Console.ForegroundColor = ConsoleColor.DarkGray;
if (e.Exceptions[e.Exceptions.Count - 1] is SqlException ex)
{
Console.WriteLine($"{ex.Number}-{ex.Message}\n");
}
else
{
Console.WriteLine($"{e.Exceptions[e.Exceptions.Count - 1].Message}\n");
}
// It is not good practice to do time-consuming tasks inside the retrying event which blocks the running task.
// Use parallel programming patterns to mitigate it.
if (e.RetryCount == provider.RetryLogic.NumberOfTries - 1)
{
Console.WriteLine("This is the last chance to execute the command before throwing the exception.");
Console.WriteLine("Press Enter when you're ready:");
Console.ReadLine();
Console.WriteLine("continue ...");
}
};
// Open a general connection.
s_generalConnection.Open();
try
{
// Assume the database is creating and other services are going to connect to it.
RetryCommand(provider);
}
catch
{
s_generalConnection.Close();
// exception is thrown if connecting to the database isn't successful.
throw;
}
s_generalConnection.Close();
}
private static void ExecuteCommand(SqlConnection cn, string command)
{
using var cmd = cn.CreateCommand();
cmd.CommandText = command;
cmd.ExecuteNonQuery();
}
private static void FindActiveSessions(SqlConnection cnn, string dbName)
{
using var cmd = cnn.CreateCommand();
cmd.CommandText = "DECLARE @query NVARCHAR(max) = '';" + Environment.NewLine +
$"SELECT @query = @query + 'KILL ' + CAST(spid as varchar(50)) + ';' FROM sys.sysprocesses WHERE dbid = DB_ID('{dbName}')" + Environment.NewLine +
"SELECT @query AS Active_sessions;";
var reader = cmd.ExecuteReader();
if (reader.Read())
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write($">> Execute the '{reader.GetString(0)}' command in SQL Server to unblock the running task.");
Console.ResetColor();
}
reader.Close();
}
Jak używać z poleceniami synchronicznymi
private static void RetryCommand(SqlRetryLogicBaseProvider provider)
{
// Change this if you already have a database with the same name in your database.
string dbName = "RetryCommand_TestDatabase";
// Subscribe a new event on retry event and discover the active sessions on a database
EventHandler<SqlRetryingEventArgs> retryEvent = (object s, SqlRetryingEventArgs e) =>
{
// Run just at first execution
if (e.RetryCount == 1)
{
FindActiveSessions(s_generalConnection, dbName);
Console.WriteLine($"Before exceeding {provider.RetryLogic.NumberOfTries} attempts.");
}
};
provider.Retrying += retryEvent;
// Create a new database.
ExecuteCommand(s_generalConnection, string.Format(CreateDatabaseFormat, dbName));
Console.WriteLine($"The '{dbName}' database is created.");
// Open a connection to the newly created database to block it from being dropped.
using var blockingCnn = new SqlConnection(string.Format(CnnStringFormat, dbName));
blockingCnn.Open();
Console.WriteLine($"Established a connection to '{dbName}' to block it from being dropped.");
Console.WriteLine($"Dropping `{dbName}`...");
// Try to drop the new database.
RetryCommandSync(provider, dbName);
Console.WriteLine("Command executed successfully.");
provider.Retrying -= retryEvent;
}
private static void RetryCommandSync(SqlRetryLogicBaseProvider provider, string dbName)
{
using var cmd = s_generalConnection.CreateCommand();
cmd.CommandText = string.Format(DropDatabaseFormat, dbName);
// 3. Assign the `provider` to the command
cmd.RetryLogicProvider = provider;
Console.WriteLine("The first attempt, before getting into the retry logic.");
cmd.ExecuteNonQuery();
}
Jak używać z poleceniami asynchronicznymi
private static void RetryCommand(SqlRetryLogicBaseProvider provider)
{
// Change this if you already have a database with the same name in your database.
string dbName = "RetryCommand_TestDatabase";
// Subscribe to the retry event and discover active sessions in a database
EventHandler<SqlRetryingEventArgs> retryEvent = (object s, SqlRetryingEventArgs e) =>
{
// Run just at first execution
if (e.RetryCount == 1)
{
FindActiveSessions(s_generalConnection, dbName);
Console.WriteLine($"Before exceeding {provider.RetryLogic.NumberOfTries} attempts.");
}
};
provider.Retrying += retryEvent;
// Create a new database.
ExecuteCommand(s_generalConnection, string.Format(CreateDatabaseFormat, dbName));
Console.WriteLine($"The '{dbName}' database is created.");
// Open a connection to the newly created database to block it from being dropped.
using var blockingCnn = new SqlConnection(string.Format(CnnStringFormat, dbName));
blockingCnn.Open();
Console.WriteLine($"Established a connection to '{dbName}' to block it from being dropped.");
Console.WriteLine("Dropping the database...");
// Try to drop the new database.
RetryCommandAsync(provider, dbName).Wait();
Console.WriteLine("Command executed successfully.");
provider.Retrying -= retryEvent;
}
private static async Task RetryCommandAsync(SqlRetryLogicBaseProvider provider, string dbName)
{
using var cmd = s_generalConnection.CreateCommand();
cmd.CommandText = string.Format(DropDatabaseFormat, dbName);
// 3. Assign the `provider` to the command
cmd.RetryLogicProvider = provider;
Console.WriteLine("The first attempt, before getting into the retry logic.");
await cmd.ExecuteNonQueryAsync();
}
Jak używać z starszymi poleceniami asynchronicznymi
Oprócz przypisania dostawcy do polecenia i wykonania polecenia można uruchomić go bezpośrednio przy użyciu następujących SqlRetryLogicBaseProvider metod:
- Execute<TResult>(Object, Func<TResult>)
- ExecuteAsync(Object, Func<Task>, CancellationToken)
- ExecuteAsync<TResult>(Object, Func<Task<TResult>>, CancellationToken)
private static void RetryCommand(SqlRetryLogicBaseProvider provider)
{
// Change this if you already have a database with the same name in your database.
string dbName = "RetryCommand_TestDatabase";
// Subscribe to the retry event and discover the active sessions in a database
EventHandler<SqlRetryingEventArgs> retryEvent = (object s, SqlRetryingEventArgs e) =>
{
// Run just at first execution
if (e.RetryCount == 1)
{
FindActiveSessions(s_generalConnection, dbName);
Console.WriteLine($"Before exceeding {provider.RetryLogic.NumberOfTries} attempts.");
}
};
provider.Retrying += retryEvent;
// Create a new database.
ExecuteCommand(s_generalConnection, string.Format(CreateDatabaseFormat, dbName));
Console.WriteLine($"The '{dbName}' database is created.");
// Open a connection to the newly created database to block it from being dropped.
using var blockingCnn = new SqlConnection(string.Format(CnnStringFormat, dbName));
blockingCnn.Open();
Console.WriteLine($"Established a connection to '{dbName}' to block it from being dropped.");
Console.WriteLine("Dropping the database...");
// Try to drop the new database.
RetryCommandBeginExecuteAsync(provider, dbName).Wait();
Console.WriteLine("Command executed successfully.");
provider.Retrying -= retryEvent;
}
private static async Task RetryCommandBeginExecuteAsync(SqlRetryLogicBaseProvider provider, string dbName)
{
using var cmd = s_generalConnection.CreateCommand();
cmd.CommandText = string.Format(DropDatabaseFormat, dbName);
// Execute the BeginExecuteXXX and EndExecuteXXX functions by using Task.Factory.FromAsync().
// Apply the retry logic by using the ExecuteAsync function of the configurable retry logic provider.
Console.WriteLine("The first attempt, before getting into the retry logic.");
await provider.ExecuteAsync(cmd, () => Task.Factory.FromAsync(cmd.BeginExecuteNonQuery(), cmd.EndExecuteNonQuery));
}
Uwaga
Model programowania asynchronicznego (APM) to starszy wzorzec, który używa pary metod rozpoczynających się od Begin
i End
i oraz interfejsu o nazwie IAsyncResult
. Nie zaleca się używania tego wzorca w nowych aplikacjach. Te metody służą do zapewnienia zgodności z poprzednimi wersjami.