Use Polly in .NET Worker Service for Reliable Database Calls

When building background processing applications in .NET, Worker Services are commonly used to handle scheduled jobs, message queue processing, data synchronization, and long-running tasks. These services often interact with databases continuously, making reliability and fault tolerance critical for production environments.

However, database operations are not always stable. Temporary network interruptions, connection timeouts, deadlocks, or transient SQL failures can cause your Worker Service to fail unexpectedly. Without proper handling, these issues may lead to application crashes, data inconsistency, or missed processing tasks.

This is where Polly becomes extremely useful. Polly is a powerful .NET resilience and transient-fault-handling library that helps developers implement retry policies, circuit breakers, timeout handling, and fallback strategies with minimal code changes.

In this article, we will learn how to use Polly in a .NET Core Worker Service to make database calls more reliable and resilient. We will implement retry policies for transient database failures and understand how Polly improves the stability of background services in real-world applications.

Use Polly in .NET Worker Service for Reliable Database Calls

Getting Started

Polly is primarily introduced as a general resilience library, and the most common examples are around HttpClient and APIs. But Polly is not limited to HTTP calls. It works with any operation in .NET, including:

  • database calls
  • file I/O
  • message queues
  • gRPC
  • Redis
  • background jobs
  • third-party SDKs
Polly for .NET is commonly used for database calls, especially to handle transient failures such as:
  • Temporary network interruptions
  • SQL deadlocks
  • Connection timeouts
  • Azure SQL throttling
  • Failover events
  • Brief database overloads
Typical resilience strategies for databases include:
  1. Retry: The Retry policy automatically retries failed database calls when transient failures occur.
      Benefits:
    • Handles temporary network failures
    • Reduces random SQL timeout issues
    • Improves application resilience
  2. Timeout: The Timeout policy prevents database operations from running indefinitely.
      Benefits:
    • Prevents hanging database calls
    • Protects thread resources
    • Improves system responsiveness
  3. Circuit breaker: The Circuit Breaker temporarily stops database calls after repeated failures.
      Benefits:
    • Prevents overwhelming the database
    • Avoids repeated failed requests
    • Gives downstream systems time to recover
Using Polly around database operations is a very standard and valid pattern when:
  • you use raw ADO.NET / Dapper
  • you need custom retry logic
  • you want circuit breakers
  • you want centralized resilience handling

Implementing Polly Strategies in .NET Worker Service with ADO.NET

In this section, we will implement Retry, Timeout, and Circuit Breaker strategies using Polly while calling a database through ADO.NET in a .NET Worker Service.

Prerequisites
    Before starting, make sure you have:
  • .NET 8 Worker Service project
  • SQL Server database
  • Basic knowledge of ADO.NET
  • Polly package installed

Install Polly package using NuGet

Before implementing resilience strategies, you need to install the Polly NuGet package in your .NET Worker Service project. Polly is a popular resilience and transient-fault-handling library for .NET that provides built-in support for Retry, Timeout, Circuit Breaker, Fallback, and other fault-handling policies. Installing this package allows your application to gracefully handle temporary database failures and improve the reliability of background services.

You can install Polly using the following .NET CLI command:

dotnet add package Polly  

Alternatively, if you are using Visual Studio, you can install it through the NuGet Package Manager by searching for the “Polly” package and adding it to your project.

Create a Worker Service Project

To begin implementing Polly in a background application, you first need to create a .NET Worker Service project. You can create a new Worker Service project using the .NET CLI with the command dotnet new worker -n PollyWorkerService, which generates the required project structure and boilerplate code.

After creation, navigate into the project folder using cd PollyWorkerService and open it in your preferred development environment. This setup provides the foundation where you can later implement database operations along with Polly’s resilience policies.

If you are using Microsoft Visual Studio then visit my previous post to create a worker service

Configure Database Connection String

  
  Add the database connection string inside appsettings.json.
  { 
  "ConnectionStrings": { 
	"DefaultConnection": "Server=YOUR_SERVER;Database=YOUR_DB;Trusted_Connection=True;TrustServerCertificate=True;" 
	} 
}

Create Polly Resilience Policies

Open Worker.cs and create Retry, Timeout, and Circuit Breaker policies.

using Microsoft.Data.SqlClient;
using Polly;
using Polly.Timeout;
using System.Data;
public class Worker : BackgroundService
{
  private readonly IConfiguration _configuration;
  private readonly ILogger<Worker> _logger;
  public Worker(IConfiguration configuration, ILogger<Worker> logger)
  {
    _configuration = configuration; _logger = logger;
  }
  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    var retryPolicy = Policy .Handle<SqlException>()
    .Or<TimeoutRejectedException>()
    .WaitAndRetryAsync(
    retryCount: 3,
    sleepDurationProvider: retryAttempt =>
    TimeSpan.FromSeconds(retryAttempt),
    onRetry: (exception, timeSpan, retryCount, context) =>
    {
      _logger.LogWarning(
      $"Retry {retryCount} after {timeSpan.TotalSeconds}s due to: {exception.Message}");
      });
      var timeoutPolicy = Policy .TimeoutAsync(TimeSpan.FromSeconds(5));
      var circuitBreakerPolicy = Policy
      .Handle<SqlException>()
      .CircuitBreakerAsync(
      exceptionsAllowedBeforeBreaking: 2,
      durationOfBreak: TimeSpan.FromSeconds(30),
      onBreak: (exception, breakDelay) =>
      {
        _logger.LogError( $"Circuit broken for {breakDelay.TotalSeconds}s due to: {exception.Message}");
        },
        onReset: () =>
        {
          _logger.LogInformation("Circuit reset.");
          });
          var combinedPolicy = Policy.WrapAsync(
          retryPolicy, circuitBreakerPolicy, timeoutPolicy);
          while (!stoppingToken.IsCancellationRequested)
          {
            try
            {
              await combinedPolicy.ExecuteAsync(async () => {
                await GetDataFromDatabaseAsync();
                });
              }
              catch (Exception ex)
              {
                _logger.LogError(ex, "Database operation failed.");
              }
              await Task.Delay(5000, stoppingToken);
            }
          }
          private async Task GetDataFromDatabaseAsync()
          {
            var connectionString = _configuration.GetConnectionString("DefaultConnection");
            using SqlConnection connection = new SqlConnection(connectionString);
            await connection.OpenAsync();
            var query = "SELECT TOP 1 * FROM Employees";
            using SqlCommand command = new SqlCommand(query, connection);
            using SqlDataReader reader = await command.ExecuteReaderAsync();
            while (await reader.ReadAsync())
            {
              Console.WriteLine(reader[0].ToString());
            }
          }
        }

If you are using Entity Framework Core, you usually do not need Polly just for database retries. EF Core already provides built-in resilient execution strategies for supported providers like SQL Server.

For SQL Server, this is typically enough:

builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
connectionString,
sqlOptions =>
{
  sqlOptions.EnableRetryOnFailure(
  maxRetryCount: 5,
  maxRetryDelay: TimeSpan.FromSeconds(30),
  errorNumbersToAdd: null);
  }));
This handles common transient failures like:
  • temporary network issues
  • Azure SQL failovers
  • throttling
  • brief connectivity interruptions

Best Practices
  • Use Retry only for transient failures
  • Keep timeout duration realistic
  • Avoid excessive retries
  • Monitor circuit breaker events with logging
  • Combine Polly with health checks and observability tools

Summary

Using Polly with ADO.NET in a .NET Worker Service significantly improves application reliability. By implementing Retry, Timeout, and Circuit Breaker strategies, you can protect your background services from transient database failures and improve system stability in production environments.

Polly provides a clean and flexible approach to resilience engineering, making it an excellent choice for enterprise-grade .NET applications.

Thanks

Kailash Chandra Behera

I am an IT professional with over 13 years of experience in the full software development life cycle for Windows, services, and web-based applications using Microsoft .NET technologies.

Previous Post Next Post

نموذج الاتصال