Da qualche anno a questa parte abbiamo visto nascere la nuova libreria Microsoft.Data.SqlClient, inizialmente “uguale” alla famosissima System.Data.SqlClient, ma sulla quale verranno portati avanti tutti i futuri sviluppi.
E’ possibile aggiornare da System.Data.SqlClient a Microsoft.Data.SqlClient senza dover eseguire refactoring al codice!
Come implementare la logica di retry?
Come prima cosa dobbiamo installare il pacchetto nuget della nuova libreria (se hai installato System.Data.SqlClient disinstallala prima di procedere)
dotnet add package Microsoft.Data.SqlClient
A questo punto siamo pronti per mettere mano al codice. Il primo passaggio è quello di andare a definire un oggetto che descriva come si dovrà comportare il SqlClient in caso di fault:
var retryLogicOptions = new SqlRetryLogicOption
{
NumberOfTries = 5,
DeltaTime = TimeSpan.FromSeconds(2),
MaxTimeInterval = TimeSpan.FromSeconds(50),
RetryLogicProvider = SqlRetryLogicOption.CreateExponentialRetryProvider(),
TransientErrors = new List<int> { 4060, 10928, 10929 }
};
Le proprietà sono:
NumberOfTries | Quante volte verrà ritentata la query |
DeltaTime | Intervallo di tempo tra i tentativi, utilizzato in combinazione con la strategia di retry. |
MaxTimeInterval | Intervallo massimo di tempo totale consentito per tutti i retry combinati. Aiuta a prevenire tentativi eccessivi. |
RetryLogicProvider | La logica di retry da utilizzare. Può essere una logica predefinita (ad esempio esponenziale o intervallo fisso) o una personalizzata. |
TransientErrors | Una collezione di codici errore SQL Server da trattare come transitori. Può essere personalizzata per gestire scenari specifici. Gli errori che sono inclusi in questa lista innescheranno la logica di retry, quelli non inseriti qui dentro invece genereranno un’exception. Definendo solamente gli errori che possono essere transitori (come una mancata connessione al database) evitiamo che il meccanismo di retry venga applicato anche ad errori che sono di altra natura (es. un errore nella query o nella store procedure) |
RetryableMethod | Indica quali metodi/operazioni SQL possono essere soggetti alla retry logic (Open, Execute, OpenAndExecute, None). Se scegliamo Open, per esempio, la retry policy gestirà solo gli errori che si verificano durante l’apertura della connessione. |
A questo punto siamo pronti per eseguire la nostra query, specificando la retry policy che abbiamo appena configurato:
using (var connection = new SqlConnection("ConnectionString"))
{
//Qui specifichiamo la logica di retry
connection.RetryLogicProvider = retryLogicOptions;
try
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM JediMasters";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine(reader[0]);
}
}
}
}
catch (SqlException ex)
{
//Exception
}
}
Quando verrà eseguito command.ExecuteReader() e l’esecuzione andrà in errore allora si attiveranno i successivi tentativi secondo questa logica:
Primo Tentativo: 2 secondi
Secondo Tentativo: 4 secondi
Terzo Tentativo: 8 secondi
Quarto Tentativo: 16 secondi
Quinto Tentativo: 32 secondi
Il totale sarà quindi 62 secondi. Nella property MaxTimeInterval abbiamo però impostato 50 secondi, questo significa che dopo questo tempo verrà generata l’exception!
Conclusioni
Abbiamo visto come rendere più resiliente il nostro codice con questa funzionalità “by design” nella nuova libreria Microsoft.Data.SqlClient, la quale ci mette a disposizione un metodo per poter gestire una serie di tentativi di ri-esecuzione delle query a fronte di determinati errori o determinati stati.
In passato mi è capitato più volte di dover gestire questa logica tramite la libreria Polly), altre volte ancora ho dovuto implementare una logica custom con dei cicli while, da oggi il mio primo pensiero sarà sicuramente quello di usare questa libreria, senza nessuna dipendenza esterna!
Se pensi che questa pillola ti sia stata utile condividila con qualche tuo collega!
Buona programmazione!