Retry failed queries with Microsoft.Data.SqlClient

In recent years, the new library Microsoft.Data.SqlClient has emerged. While initially similar to the well-known System.Data.SqlClient, it is now the focus for all future developments.

You can upgrade from System.Data.SqlClient to Microsoft.Data.SqlClient without requiring any code refactoring!

How to implement retry logic?

To implement retry logic, start by installing the NuGet package for the new library. If you have System.Data.SqlClient installed, make sure to uninstall it before proceeding.

C#
dotnet add package Microsoft.Data.SqlClient

Now we are ready to dive into the code. The first step is to define an object that specifies how the SqlClient should handle faults:

C#
var retryLogicOptions = new SqlRetryLogicOption
        {
            NumberOfTries = 5,
            DeltaTime = TimeSpan.FromSeconds(2),
            MaxTimeInterval = TimeSpan.FromSeconds(50),
            RetryLogicProvider = SqlRetryLogicOption.CreateExponentialRetryProvider(),
            TransientErrors = new List<int> { 4060, 10928, 10929 }
        };

Properties:

NumberOfTriesDefines how many times the query will be retried in case of a failure.
DeltaTimeSpecifies the delay between retry attempts, in line with the chosen retry strategy.
MaxTimeIntervalDefines the maximum allowable time for all retries, ensuring retries remain within a reasonable limit.
RetryLogicProviderDefines the retry logic to use. Options include predefined strategies (e.g., exponential or fixed interval) or a custom implementation.
TransientErrorsA list of SQL Server error codes treated as transient, which can be customized for specific use cases.
rrors in this list will trigger the retry logic, while those not included will result in an exception. Focusing on transient errors (e.g., a lost database connection) ensures the retry logic is not triggered by non-transient issues like query syntax errors or stored procedure bugs.
RetryableMethodDefines which SQL methods or operations are subject to retry logic. Available options are: Open, Execute, OpenAndExecute, None. Open, for example, applies retry logic to errors encountered when opening a connection.

We are now ready to execute our query using the retry policy we just configured:

C#
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
    }
}

When command.ExecuteReader() is executed and an error occurs, subsequent attempts will follow this logic:

First Attempt: 2 seconds

Second Attempt: 4 seconds

Third Attempt: 8 seconds

Fourth Attempt: 16 seconds

Fifth Attempt: 32 seconds

The total retry duration will be 62 seconds. However, since the MaxTimeInterval property is set to 50 seconds, an exception will be thrown once this limit is reached.

Conclusioni

We have explored how to make our code more resilient using the “by design” retry feature in the new Microsoft.Data.SqlClient library. This functionality allows us to handle query retries efficiently in response to specific errors or states.

In the past, I often managed retry logic using the Polly library, and at times, I implemented custom solutions with while loops. Moving forward, this library will undoubtedly be my go-to choice, as it eliminates the need for external dependencies!

If you found this information helpful, feel free to share it with your colleagues.

Happy coding!

Share this article
Shareable URL
Next Post

LINQ Extension Method

Leave a Reply

Your email address will not be published. Required fields are marked *

Read next

Discovering Span<T>

With .NET Core 2.1 and C# 7.2, a new type of struct was introduced: Span<T> and ReadOnlySpan<T>.…

.NET 9 Hybrid Cache

In this article, we’ll delve into one of Microsoft’s latest innovations, currently in preview at the time of…

HashSet in .NET 9

What is a HashSet<T>? In .NET, a HashSet<T> is a collection that implements an unordered set of unique…