PollySqlClient 1.0.1
dotnet add package PollySqlClient --version 1.0.1
NuGet\Install-Package PollySqlClient -Version 1.0.1
<PackageReference Include="PollySqlClient" Version="1.0.1" />
<PackageVersion Include="PollySqlClient" Version="1.0.1" />
<PackageReference Include="PollySqlClient" />
paket add PollySqlClient --version 1.0.1
#r "nuget: PollySqlClient, 1.0.1"
#:package PollySqlClient@1.0.1
#addin nuget:?package=PollySqlClient&version=1.0.1
#tool nuget:?package=PollySqlClient&version=1.0.1
PollySqlClient
Polly v8 resilience for SQL Server and Azure SQL — retry, timeout, and circuit-breaker for SqlConnection queries and commands, plus a built-in SqlServerTransientErrors predicate covering all common SQL Server and Azure SQL transient error numbers. Zero changes to your existing SQL.
// Before
await cmd.ExecuteNonQueryAsync();
// After — automatic retry + timeout on every operation
var resilient = connection.WithPolly(pipeline =>
pipeline
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
ShouldHandle = SqlServerTransientErrors.IsTransient, // built-in ✔
})
.AddTimeout(TimeSpan.FromSeconds(30)));
await resilient.ExecuteAsync("INSERT INTO Orders (CustomerId) VALUES (@id)",
parameters: [new SqlParameter("@id", customerId)]);
Installation
dotnet add package PollySqlClient
Targets net6.0, net8.0, and net9.0.
Dependencies: Polly.Core 8.*, Microsoft.Data.SqlClient 6.*, Microsoft.Extensions.DependencyInjection.Abstractions 8.*
SqlServerTransientErrors — the key feature
Knowing which SQL Server errors are safe to retry is the hard part. PollySqlClient ships a pre-built SqlServerTransientErrors.IsTransient predicate so you don't have to look up error numbers.
new RetryStrategyOptions
{
MaxRetryAttempts = 3,
ShouldHandle = SqlServerTransientErrors.IsTransient,
}
SQL Server errors
| Error | Description |
|---|---|
1205 |
Deadlock victim |
1204 |
Instance ran out of locks |
233 |
Connection does not exist (named pipes) |
64 |
Connection lost during login |
10053 |
Transport-level error receiving from server |
10054 |
Transport-level error sending to server |
10060 |
Network error / server not found |
Azure SQL errors
| Error | Description |
|---|---|
40613 |
Database not currently available — retry |
40501 |
Service busy — retry after 10 seconds |
40197 |
Error processing request — retry |
49920 |
Too many operations in progress |
49919 |
Too many create/update operations |
49918 |
Not enough resources to process request |
10929 |
Resource limit reached — retry later |
10928 |
Resource limit hit |
4221 |
Login to read-secondary failed |
The raw set is also available for extension:
var myErrors = SqlServerTransientErrors.ErrorNumbers.ToHashSet();
myErrors.Add(4060); // Cannot open database (sometimes transient)
new RetryStrategyOptions
{
ShouldHandle = new PredicateBuilder().Handle<SqlException>(ex =>
ex.Errors.Cast<SqlError>().Any(e => myErrors.Contains(e.Number)))
}
Quick start
Inline pipeline
using PollySqlClient;
await using var connection = new SqlConnection(connectionString);
var resilient = connection.WithPolly(pipeline =>
pipeline
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromMilliseconds(200),
BackoffType = DelayBackoffType.Exponential,
ShouldHandle = SqlServerTransientErrors.IsTransient,
})
.AddTimeout(TimeSpan.FromSeconds(30)));
await resilient.OpenAsync();
// Non-query
await resilient.ExecuteAsync(
"INSERT INTO Events (Type, Payload) VALUES (@type, @payload)",
parameters: [new("@type", "OrderPlaced"), new("@payload", json)]);
// Query with mapper
var orders = await resilient.QueryAsync(
"SELECT Id, Total FROM Orders WHERE CustomerId = @id",
reader => new Order(reader.GetInt32(0), reader.GetDecimal(1)),
parameters: [new SqlParameter("@id", customerId)]);
// Scalar
var count = await resilient.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM Orders");
Dependency injection
// Program.cs
builder.Services.AddScoped(_ => new SqlConnection(connectionString));
builder.Services.AddPollySqlClient(pipeline =>
pipeline
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromMilliseconds(200),
BackoffType = DelayBackoffType.Exponential,
ShouldHandle = SqlServerTransientErrors.IsTransient,
})
.AddTimeout(TimeSpan.FromSeconds(30))
.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
FailureRatio = 0.5,
MinimumThroughput = 10,
SamplingDuration = TimeSpan.FromSeconds(30),
BreakDuration = TimeSpan.FromSeconds(15),
}));
// Repository
public class OrderRepository(SqlConnection db, ResiliencePipeline pipeline)
{
public async Task<List<Order>> GetByCustomerAsync(int customerId)
{
var resilient = db.WithPolly(pipeline);
await resilient.OpenAsync();
return await resilient.QueryAsync(
"SELECT Id, Total FROM Orders WHERE CustomerId = @id",
r => new Order(r.GetInt32(0), r.GetDecimal(1)),
parameters: [new SqlParameter("@id", customerId)]);
}
}
Supported operations
| Method | Description |
|---|---|
OpenAsync |
Open the connection with retry |
ExecuteAsync |
Execute non-query, returns rows affected |
ExecuteScalarAsync<T> |
Execute scalar, returns first column of first row |
QueryAsync<T> |
Query with row mapper, returns List<T> |
QueryFirstOrDefaultAsync<T> |
Query with row mapper, returns first row or default |
Pipeline order
[Timeout] → [Retry] → [Circuit Breaker] → [SqlClient]
pipeline
.AddTimeout(TimeSpan.FromSeconds(30)) // 1. Overall deadline
.AddRetry(retryOptions) // 2. Retry transient failures
.AddCircuitBreaker(cbOptions) // 3. Open circuit under load
Related packages
| Package | Downloads | Description |
|---|---|---|
| PollyNpgsql | Polly v8 resilience for Npgsql (PostgreSQL) with PostgresTransientErrors predicate | |
| PollyDapper | Polly v8 resilience for Dapper (works with any IDbConnection) | |
| PollyEFCore | Polly v8 resilience for EF Core queries and SaveChanges | |
| PollyMongo | Polly v8 resilience for MongoDB.Driver | |
| PollyAzureBlob | Polly v8 resilience for Azure Blob Storage | |
| PollyAzureEventHub | Polly v8 for Azure Event Hubs | |
| PollyAzureServiceBus | Polly v8 resilience for Azure Service Bus | |
| PollyRedis | Polly v8 resilience for StackExchange.Redis | |
| PollyMediatR | Polly v8 resilience for MediatR | |
| PollyOpenAI | Polly v8 resilience for OpenAI and Azure OpenAI | |
| PollyHealthChecks | ASP.NET Core health checks for Polly v8 circuit breakers | |
| PollyElasticsearch | Polly v8 for Elastic.Clients.Elasticsearch | |
| PollyAzureKeyVault | Polly v8 for Azure Key Vault | |
| PollySendGrid | Polly v8 for SendGrid | |
| PollyMassTransit | Polly v8 for MassTransit | |
| PollyAzureTableStorage | Polly v8 for Azure Table Storage | |
| PollyMailKit | MailKit SMTP email client | |
| PollyAzureQueueStorage | Azure Queue Storage QueueClient | |
| PollyHangfire | Hangfire IBackgroundJobClient | |
| PollyBackoff | Jitter, linear & custom backoff for Polly v8 retry |
💼 Need .NET consulting?
The author of this package is available for consulting on Polly v8 resilience, Azure cloud architecture, and clean .NET design.
→ solidqualitysolutions.com · LinkedIn
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- Microsoft.Data.SqlClient (>= 6.1.6)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.17)
- Polly.Core (>= 8.7.0)
-
net8.0
- Microsoft.Data.SqlClient (>= 6.1.6)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.17)
- Polly.Core (>= 8.7.0)
-
net9.0
- Microsoft.Data.SqlClient (>= 6.1.6)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.17)
- Polly.Core (>= 8.7.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.0.1: Fix package icon (was showing an incorrect/placeholder image). 1.0.0: Initial release. ResilientSqlConnection wraps SqlConnection queries and commands in a Polly v8 ResiliencePipeline. Includes SqlServerTransientErrors helper with pre-built ShouldHandle predicate covering SQL Server and Azure SQL transient error numbers. Supports net6.0, net8.0, and net9.0.