LiteRetry 0.0.7

There is a newer version of this package available.
See the version list below for details.
dotnet add package LiteRetry --version 0.0.7
                    
NuGet\Install-Package LiteRetry -Version 0.0.7
                    
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="0.0.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LiteRetry" Version="0.0.7" />
                    
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 0.0.7
                    
#r "nuget: LiteRetry, 0.0.7"
                    
#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.
#:package LiteRetry@0.0.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LiteRetry&version=0.0.7
                    
Install as a Cake Addin
#tool nuget:?package=LiteRetry&version=0.0.7
                    
Install as a Cake Tool

LiteRetry โœจ

NuGet version License: MIT

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.
  • 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.
  • 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).
  • 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>()
            .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>()
                .OnRetryAsync(ctx =>
                {
                    Console.WriteLine($"Attempt {ctx.AttemptNumber} failed. Retrying after {ctx.Delay.TotalMilliseconds}ms...");
                    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.AttemptNumber} after {ctx.Delay.TotalMilliseconds}ms due to {ctx.LastException.GetType().Name}");
            return Task.CompletedTask;
        },
        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 147 5/8/2025
0.0.12 139 5/5/2025
0.0.11 142 5/4/2025
0.0.10 136 5/4/2025
0.0.9 140 5/4/2025
0.0.8 109 5/4/2025
0.0.7 100 5/4/2025
0.0.6 102 5/4/2025
0.0.5 107 5/4/2025
0.0.4 103 5/4/2025
0.0.3 67 5/3/2025
0.0.2 74 5/3/2025
0.0.1 71 5/3/2025