Polly v8 resilience pipelines for Dapper — wrap QueryAsync, ExecuteAsync, and other Dapper calls with retry, timeout, circuit-breaker, and more using a single ResilientDbConnection decorator. Zero changes to your SQL.
var resilient = connection.WithPolly(pipeline =>
pipeline
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromMilliseconds(200),
ShouldHandle = new PredicateBuilder().Handle<Exception>(),
})
.AddTimeout(TimeSpan.FromSeconds(5)));
var orders = await resilient.QueryAsync<Order>("SELECT * FROM Orders WHERE CustomerId = @Id", new { Id = id });Every Dapper call is now automatically wrapped with retry + timeout — zero changes to existing SQL.
Dapper is intentionally minimal — it gives you no interception point for cross-cutting concerns like retry or timeout. PollyDapper adds that layer cleanly.
| Without PollyDapper | With PollyDapper |
|---|---|
| Write try/catch + retry loops around every query | One WithPolly(...) call |
Manually pass CancellationToken for timeouts |
Timeout managed by the pipeline |
| Duplicate retry logic across repositories | Single pipeline applied everywhere |
| Must touch every query to add resilience | Zero changes to existing SQL |
dotnet add package PollyDapperTargets net6.0, net8.0, and net9.0.
Dependencies: Polly.Core 8.*, Dapper 2.*, Microsoft.Extensions.DependencyInjection.Abstractions 8.*
using PollyDapper;
var resilient = connection.WithPolly(pipeline =>
pipeline.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromMilliseconds(200),
BackoffType = DelayBackoffType.Exponential,
ShouldHandle = new PredicateBuilder().Handle<Exception>(),
}));
var users = await resilient.QueryAsync<User>("SELECT * FROM Users");var pipeline = new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3 })
.AddTimeout(TimeSpan.FromSeconds(10))
.Build();
var resilient = connection.WithPolly(pipeline);
var count = await resilient.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM Orders");// Program.cs
builder.Services.AddPollyDapper(pipeline =>
pipeline
.AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3 })
.AddTimeout(TimeSpan.FromSeconds(5)));
// Repository
public class OrderRepository(IDbConnection db, ResiliencePipeline pipeline)
{
public Task<IEnumerable<Order>> GetAllAsync() =>
db.WithPolly(pipeline).QueryAsync<Order>("SELECT * FROM Orders");
public Task<int> InsertAsync(Order order) =>
db.WithPolly(pipeline).ExecuteAsync(
"INSERT INTO Orders (CustomerId, Total) VALUES (@CustomerId, @Total)", order);
}| Method | Description |
|---|---|
QueryAsync<T> |
Returns IEnumerable<T> |
QueryFirstAsync<T> |
First row, throws if empty |
QueryFirstOrDefaultAsync<T> |
First row or default |
QuerySingleAsync<T> |
Exactly one row, throws otherwise |
QuerySingleOrDefaultAsync<T> |
One row or default |
ExecuteAsync |
Rows affected |
ExecuteScalarAsync<T> |
First column of first row |
Polly strategies are applied outer-to-inner (left-to-right). The recommended order is:
[Timeout] → [Retry] → [Circuit Breaker] → [Dapper]
pipeline
.AddTimeout(TimeSpan.FromSeconds(10)) // 1. Overall deadline
.AddRetry(retryOptions) // 2. Retry on failure
.AddCircuitBreaker(cbOptions) // 3. Open circuit if overloadedvar builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IDbConnection>(_ =>
new SqlConnection(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddPollyDapper(pipeline =>
pipeline
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromMilliseconds(100),
BackoffType = DelayBackoffType.Exponential,
ShouldHandle = new PredicateBuilder()
.Handle<SqlException>(ex => ex.IsTransient),
})
.AddTimeout(TimeSpan.FromSeconds(30))
.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
FailureRatio = 0.5,
MinimumThroughput = 10,
SamplingDuration = TimeSpan.FromSeconds(30),
BreakDuration = TimeSpan.FromSeconds(15),
}));| Package | Downloads | Description |
|---|---|---|
| PollyHealthChecks | ASP.NET Core health checks for Polly v8 circuit breakers — expose circuit-breaker state (Closed, HalfOpen, Open, Isolated) as /health endpoint responses | |
| PollyBackoff | Backoff delay strategies for Polly v8 resilience pipelines | |
| PollyEFCore | Polly v8 resilience pipelines for Entity Framework Core — wrap every EF Core query and SaveChanges with retry, timeout and circuit-breaker via a single AddPollyResilience() call | |
| PollyMailKit | Polly v8 resilience pipelines for MailKit — retry, timeout, and circuit-breaker for SmtpClient.SendAsync and any MailKit SMTP operation | |
| PollyMassTransit | Polly v8 resilience pipelines for MassTransit — retry, timeout, and circuit-breaker for IBus.Publish and ISendEndpointProvider.Send | |
| PollyAzureEventHub | Polly v8 resilience pipelines for Azure Event Hubs — retry, timeout, and circuit-breaker for EventHubProducerClient and EventHubConsumerClient | |
| PollyOpenAI | Polly v8 resilience for OpenAI and Azure OpenAI API calls | |
| PollySignalR | Polly v8 reconnect policy for SignalR | |
| PollyElasticsearch | Polly v8 resilience pipelines for Elastic.Clients.Elasticsearch 8+ — retry, timeout, and circuit-breaker for any Elasticsearch operation, plus a built-in ElasticTransientErrors predicate covering rate limiting (429), service unavailability (503), gateway timeouts (504), and connection failures | |
| PollyHangfire | Polly v8 resilience pipelines for Hangfire — retry, timeout, and circuit-breaker for IBackgroundJobClient.Enqueue and Schedule | |
| PollySendGrid | Polly v8 resilience pipelines for SendGrid — retry, timeout, and circuit-breaker for ISendGridClient.SendEmailAsync | |
| PollyMediatR | Polly v8 resilience pipelines for MediatR — add retry, timeout, circuit-breaker, rate-limiting, hedging, and chaos engineering to any MediatR request handler with a single line of DI registration | |
| PollyAzureKeyVault | Polly v8 resilience pipelines for Azure Key Vault — retry, timeout, and circuit-breaker for SecretClient, KeyClient, and CertificateClient | |
| PollyAzureQueueStorage | Polly v8 resilience pipelines for Azure Queue Storage — retry, timeout, and circuit-breaker for Azure.Storage.Queues QueueClient | |
| PollySqlClient | Polly v8 resilience pipelines for Microsoft.Data.SqlClient (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 | |
| PollyRedis | Polly v8 resilience for StackExchange.Redis | |
| PollyAzureTableStorage | Polly v8 resilience pipelines for Azure Table Storage — retry, timeout, and circuit-breaker for Azure.Data.Tables TableClient | |
| PollyChaos | Chaos engineering and fault-injection resilience strategies for Polly v8 pipelines |
The author of this package is available for consulting on Polly v8 resilience, Azure cloud architecture, and clean .NET design.
→ solidqualitysolutions.com · LinkedIn
MIT