Polly v8 resilience pipelines for MailKit SMTP — retry, timeout, and circuit-breaker for sending email via SmtpClient.
SMTP is inherently flaky. Connections drop, servers throttle, and ServiceNotConnectedException is thrown mid-send. PollyMailKit wraps MailKit's SmtpClient in a Polly v8 ResiliencePipeline so you get automatic retry, exponential back-off, and circuit-breaking with zero boilerplate.
Two wrapper styles:
- ResilientSmtpSender — creates a fresh connection per attempt (fully retry-safe; recommended for transient failures)
- ResilientSmtpClient — wraps an existing connected SmtpClient (preferred when the connection lifecycle is managed externally)
dotnet add package PollyMailKit
`csharp // Configure in DI services.AddPollyMailKit( new SmtpSettings("smtp.example.com", 587, "user@example.com", "password"), pipeline => pipeline .AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3, Delay = TimeSpan.FromSeconds(1), BackoffType = DelayBackoffType.Exponential, ShouldHandle = MailKitTransientErrors.IsTransient, }) .AddTimeout(TimeSpan.FromSeconds(30)));
// Use via DI public class EmailService(ResilientSmtpSender sender) { public async Task SendAsync(MimeMessage message, CancellationToken ct = default) => await sender.SendAsync(message, ct); } `
`csharp var smtpClient = new SmtpClient(); await smtpClient.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.Auto); await smtpClient.AuthenticateAsync("user", "password");
var resilient = smtpClient.WithPolly(pipeline => pipeline.AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3, Delay = TimeSpan.FromSeconds(1), BackoffType = DelayBackoffType.Exponential, ShouldHandle = MailKitTransientErrors.IsTransient, }));
await resilient.SendAsync(message); `
csharp public record SmtpSettings( string Host, int Port, string UserName, string Password, SecureSocketOptions SecureSocketOptions = SecureSocketOptions.Auto);
MailKitTransientErrors.IsTransient handles:
| Exception | Reason |
|---|---|
| SocketException | Network-level failure |
| IOException | Stream read/write error |
| ServiceNotConnectedException | SMTP server disconnected mid-session |
| OperationCanceledException | Timeout or cancellation |
et6.0 · et8.0 · et9.0
| Package | Wraps |
|---|---|
| PollyEFCore | Entity Framework Core DbContext |
| PollyDapper | Dapper IDbConnection |
| PollyMongo | MongoDB IMongoCollection |
| PollyAzureBlob | Azure Blob Storage BlobContainerClient |
| PollyNpgsql | Npgsql PostgreSQL NpgsqlConnection |
| PollySqlClient | System.Data.SqlClient SqlConnection |
| PollyCosmosDb | Azure Cosmos DB CosmosClient |
| PollyGrpc | gRPC channel calls |
| PollyRabbitMQ | RabbitMQ IModel channel |
| PollyAzureServiceBus | Azure Service Bus sender/receiver |
| PollyRedis | StackExchange.Redis IDatabase |
| PollyMediatR | MediatR IMediator |
| PollyOpenAI | OpenAI ChatClient |
| PollyHealthChecks | ASP.NET Core health checks |
| PollyBackoff | Pre-built backoff pipelines |
| PollyChaos | Chaos engineering helpers |
| PollyKafka | Confluent Kafka producer/consumer |
| PollySignalR | SignalR HubConnection |
| PollyRateLimiter | .NET rate limiting middleware |
| PollyElasticsearch | Elastic.Clients.Elasticsearch |
| PollyAzureKeyVault | Azure Key Vault SecretClient |
| PollyAzureEventHub | Azure Event Hubs producer |
| PollySendGrid | SendGrid email client |
| PollyMassTransit | MassTransit IBus |
| PollyAzureTableStorage | Azure Table Storage TableClient |
| PollyAzureQueueStorage | Azure Queue Storage QueueClient |
| PollyHangfire | Hangfire IBackgroundJobClient |
The author of this package is available for consulting on Polly v8 resilience, Azure cloud architecture, and clean .NET design.
→ solidqualitysolutions.com · LinkedIn
MIT © Justin Bannister