LiteRetry 1.0.0

dotnet add package LiteRetry --version 1.0.0
                    
NuGet\Install-Package LiteRetry -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LiteRetry" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LiteRetry" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="LiteRetry" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LiteRetry --version 1.0.0
                    
#r "nuget: LiteRetry, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=LiteRetry&version=1.0.0
                    
Install LiteRetry as a Cake Addin
#tool nuget:?package=LiteRetry&version=1.0.0
                    
Install LiteRetry as a Cake Tool

LiteRetry ✨

NuGet version License: MIT codecov

LiteRetry is a lightweight, fluent, and extensible retry utility for .NET. It helps developers eliminate repetitive try/catch blocks and build resilient code with ease when dealing with transient failures.


🤔 Why LiteRetry?

Modern applications often interact with external services (APIs, databases, etc.) over networks that can be unreliable. Operations might fail temporarily due to network glitches, rate limiting, temporary service unavailability, or deadlocks. Instead of letting these transient errors fail the entire operation, a common pattern is to retry.

LiteRetry provides a clean, configurable, and easy-to-use way to implement this retry logic without cluttering your core business code.


🚀 Installation

Install LiteRetry via the .NET CLI:

dotnet add package LiteRetry

Or via the NuGet Package Manager Console:

Install-Package LiteRetry

✨ Features

  • Fluent Configuration: Intuitive API using RetryBuilder for setting up retry logic.
  • Fluent Configuration: Intuitive API using RetryBuilder for setting up retry logic.
  • Direct Execution: Optional static RetryExecutor for simpler use cases.
  • Async First: Built for modern asynchronous programming (Task and Task<T>).
  • Configurable Retries: Define the maximum number of attempts.
  • Total Timeout: Optionally abort the retry process if a total time limit is exceeded.
  • Delay Strategies:
    • Fixed: Constant delay between retries.
    • Exponential: Delay increases exponentially.
    • ExponentialWithJitter: Exponential delay with added randomness to help prevent the "thundering herd" problem under high contention.
  • Exception Filtering: Retry only on specific exceptions using type (WithFilterByType<TException>) or a custom predicate (WithFilterByPredicate).
  • Retry Hook: Execute asynchronous actions (OnRetryAsync) before each retry attempt (e.g., for logging, metrics).
  • Success Hook: Execute asynchronous actions (OnSuccessAsync) after a successful attempt, even after retries.
  • Failure Hook: Execute asynchronous actions (OnFailureAsync) after all retry attempts have failed.
  • Cancellation Support: Gracefully cancel operations and pending retries using CancellationToken.
  • Detailed Results: RetryResult<T> provides information on success/failure, final value, attempts, timing, and the final exception.
  • Reliable: Fully unit-tested.

🛠️ Usage Examples

The primary way to use LiteRetry is via the fluent RetryBuilder.

Example 1: Basic Retry for Task<T>

using LiteRetry.Core.Retrying.Fluent;
using LiteRetry.Core.Retrying.Application.Enums;
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class DataFetcher
{
    private readonly HttpClient _httpClient = new HttpClient();
    private int _attemptCount = 0;

    public async Task<string> GetDataWithRetriesAsync(string url, CancellationToken cancellationToken = default)
    {
        _attemptCount = 0;

        Func<CancellationToken, Task<string>> fetchOperation = async (CancellationToken ct) =>
        {
            _attemptCount++;
            Console.WriteLine($"Attempt {_attemptCount}: Fetching {url}...");
            if (_attemptCount < 2)
                throw new HttpRequestException("Simulated network error");

            HttpResponseMessage response = await _httpClient.GetAsync(url, ct);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync(ct);
        };

        RetryResult<string> result = await Retry.Configure()
            .WithMaxAttempts(3)
            .WithBaseDelay(TimeSpan.FromMilliseconds(500))
            .WithFilterByType<HttpRequestException>()
            .WithTimeout(TimeSpan.FromSeconds(2))
            .OnSuccessAsync(ctx => {
                Console.WriteLine($"Operation succeeded on attempt {ctx.Attempt}.");
                return Task.CompletedTask;
            })
            .OnFailureAsync(ctx => {
                Console.WriteLine($"Operation failed after {ctx.Attempt} attempts.");
                return Task.CompletedTask;
            })
            .RunAsync(fetchOperation, cancellationToken);

        if (result.Succeeded)
        {
            Console.WriteLine($"Success after {result.Attempts} attempts!");
            return result.Value;
        }
        else
        {
            Console.WriteLine($"Operation failed after {result.Attempts} attempts.");
            Console.Error.WriteLine($"Error: {result.FinalException?.InnerException?.Message}");
            throw result.FinalException ?? new Exception("Retry failed for unknown reason.");
        }
    }
}

Example 2: Handling Task (Void) Operations

using LiteRetry.Core.Retrying.Fluent;
using LiteRetry.Core.Retrying.Domain;
using System;
using System.Threading;
using System.Threading.Tasks;

public class TaskProcessor
{
    private int _processAttempt = 0;

    public async Task ProcessSomethingWithRetryAsync(CancellationToken cancellationToken = default)
    {
        _processAttempt = 0;

        Func<CancellationToken, Task> processOperation = async (CancellationToken ct) =>
        {
            _processAttempt++;
            Console.WriteLine($"Attempt {_processAttempt}: Processing...");
            await Task.Delay(200, ct);
            if (_processAttempt < 3)
                throw new TimeoutException("Simulated processing timeout");

            Console.WriteLine("Processing completed successfully.");
        };

        try
        {
            IRetryExecutor retryExecutor = Retry.Configure()
                .WithMaxAttempts(4)
                .WithBaseDelay(TimeSpan.FromMilliseconds(300))
                .WithStrategy(DelayStrategy.Fixed)
                .WithFilterByType<TimeoutException>()
                .WithTimeout(TimeSpan.FromSeconds(2))
                .OnRetryAsync(ctx => {
                    Console.WriteLine($"Attempt {ctx.Attempt} failed. Retrying after {ctx.Delay.TotalMilliseconds}ms...");
                    return Task.CompletedTask;
                })
                .OnSuccessAsync(ctx => {
                    Console.WriteLine($"Successfully completed after {ctx.Attempt} attempts.");
                    return Task.CompletedTask;
                })
                .OnFailureAsync(ctx => {
                    Console.WriteLine($"Operation failed after {ctx.Attempt} attempts.");
                    return Task.CompletedTask;
                });

            await retryExecutor.RunAsync(processOperation, cancellationToken);

            Console.WriteLine("Task succeeded!");
        }
        catch (RetryFailedException ex)
        {
            Console.Error.WriteLine($"Task failed definitively after {ex.Attempts} attempts.");
            Console.Error.WriteLine($"Last error: {ex.InnerException?.Message}");
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Operation was cancelled.");
        }
    }
}

Example 3: Using RetryExecutor Directly (Alternative)

using LiteRetry.Core.Retrying.Application;
using LiteRetry.Core.Retrying.Domain;
using LiteRetry.Core.Retrying.Application.Enums;
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net.Http;

Func<CancellationToken, Task<string>> myOperation = async ct =>
{
    Console.WriteLine("Executing operation via RetryExecutor...");
    await Task.Delay(100, ct);
    return await Task.FromResult("Executor Result");
};

try
{
    RetryResult<string> result = await RetryExecutor.ExecuteAsync<string>
    (
        operation: myOperation,
        maxAttempts: 5,
        baseDelay: TimeSpan.FromMilliseconds(200),
        delayStrategy: DelayStrategy.ExponentialWithJitter,
        shouldRetry: ex => ex is TimeoutException || ex is HttpRequestException,
        onRetryAsync: ctx =>
        {
            Console.WriteLine($"RetryExecutor: Retry #{ctx.Attempt} after {ctx.Delay.TotalMilliseconds}ms due to {ctx.LastException.GetType().Name}");
            return Task.CompletedTask;
        },
        onSuccessAsync: ctx =>
        {
            Console.WriteLine($"RetryExecutor: Operation succeeded on attempt {ctx.Attempt}.");
            return Task.CompletedTask;
        },
        onFailureAsync: ctx =>
        {
            Console.WriteLine($"RetryExecutor: Operation failed after {ctx.Attempt} attempts.");
            return Task.CompletedTask;
        },
        totalTimeout: TimeSpan.FromSeconds(3),
        cancellationToken: CancellationToken.None
    );

    if (result.Succeeded)
        Console.WriteLine($"RetryExecutor succeeded: {result.Value}");
    else
        Console.WriteLine($"RetryExecutor failed after {result.Attempts} attempts.");
}
catch (OperationCanceledException)
{
    Console.WriteLine("RetryExecutor operation cancelled.");
}

📆 API Overview

Key Classes & Enums

  • RetryBuilder: Fluent API for configuring and executing retry logic.
  • RetryExecutor: Static class with ExecuteAsync methods.
  • RetryContext:
    • Attempt
    • LastException
    • Delay
    • StartTime
  • RetryResult<T>:
    • Succeeded
    • Value
    • Attempts
    • ElapsedTime
    • LastAttemptDuration
    • FinalException
  • RetryFailedException: Thrown when all attempts fail.
  • DelayStrategy: Fixed, Exponential, ExponentialWithJitter.

📜 License

Distributed under the MIT License. See LICENSE file for more information.


🙌 Author

Created by Javier Angosto Barjollo

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0 137 5/8/2025
0.0.12 135 5/5/2025
0.0.11 139 5/4/2025
0.0.10 132 5/4/2025
0.0.9 134 5/4/2025
0.0.8 106 5/4/2025
0.0.7 95 5/4/2025
0.0.6 98 5/4/2025
0.0.5 104 5/4/2025
0.0.4 98 5/4/2025
0.0.3 63 5/3/2025
0.0.2 67 5/3/2025
0.0.1 60 5/3/2025

**Release (v1.0.0)** ⚡

Provides a lightweight and flexible retry mechanism for transient fault handling in .NET applications.

**Key Features:**

* Retry any `Func<Task<T>>` or `Func<T>` operations with minimal setup.
* Supports configurable maximum retry attempts and delay strategies.
* Built-in support for exponential backoff and fixed delay.
* Offers both synchronous and asynchronous retry execution (`Retry.Run()` and `Retry.RunAsync()`).
* Predicate-based retry filtering: control which exceptions or results should trigger retries.
* Simple and fluent API for improved readability and quick integration.
* Lightweight implementation with zero external dependencies.
* Targets modern .NET versions including .NET Standard 2.0 for broad compatibility.

Ideal for scenarios like transient HTTP failures, flaky I/O operations, or external service calls.

See the README for configuration examples and advanced usage.