Skip to main content

Offload heavy processing from your requests to separate scope. Simple configuration and usage.

Total Downloads
1.9K
1,927
Published
May 16, 2024
2y ago

Get Started

dotnet add package Offloader --version 1.0.7
ProductVersions
.NET
net8.0

README

Offloader

NuGet license

A convenient way to unload some heavy work to background service from your main requests.

Install as NuGet package:

Install-Package Offloader
<PackageReference Include="Offloader" Version="1.0.*" />

Configurables

Processor func or service

// func
services.AddOffload<OffloadItemType>(x => x.UseItemProcessor(serviceProvider, item, ct) => ...);

// service, must implement IOffloadItemProcessor
services.AddOffload<OffloadItemType, YourServiceProcessorType>();

Degree of Parallelism

// default value is 1
x.UseDegreeOfParallelism(5)

Custom error logging

x.UseErrorLogger((logger, item, exception) => logger.LogError(ex, "Log in any format {WithValues}", item.Value))

Samples

Full samples can be found in Examples directory.

Simplest sample

var builder = WebApplication.CreateBuilder(args);

// ...

builder.Services.AddOffload<OffloadItemType>(x => x.UseItemProcessor((_, item, _) => 
    {
        Console.WriteLine($"Processing item with value {item.State}");
        return Task.CompletedTask;
    })
});

// ...
    
var app = builder.Build();

// use offloader in your handlers or in your controllers
app.MapPost(
    "/my-heavy-endpoint", 
    (string itemValue, IOffloader<OffloadItemType> x) => x.OffloadAsync(new OffloadItemType(itemState)));

public record OffloadItemType(string State);

Sample with inline processor function

var builder = WebApplication.CreateBuilder(args);

// ...

// register offloader function for specified Type (in this sample it's OffloadItemType)
builder.Services.AddOffload<OffloadItemType>(options =>
{
    options
        // specify processor func (required)
        .UseItemProcessor(async (provider, item, ct) =>
        {
            // request any registered service (scoped should work fine)
            // e.g. var http = provider.GetRequiredService<IHttpClientFactory>();

            // process item one by one
            Console.WriteLine($"Processing item with value {item.Value}");
            await Task.Delay(2000, ct);
        })
        
        // specify error handler, e.g. logger (optional)
        .UseErrorLogger((logger, item, ex) => logger.LogError(ex, "Log in any format {WithValues}", item.Value))
        
        // specify degree of parallelism (optional, default is 1)
        .UseDegreeOfParallelism(2);
});

// ...
    
var app = builder.Build();

// use offloader in your handlers or in your controllers
app.MapPost(
    "/my-heavy-endpoint", 
    async (string itemValue, IOffloader<OffloadItemType> offloader) =>
    {
        // do some work before returning response to the client
        
        // offload heavy work to background service
        await offloader.OffloadAsync(new OffloadItemType(itemValue)); 

        return Results.Created();
    });

public record OffloadItemType(string Value);

Sample with processor as a service class

var builder = WebApplication.CreateBuilder(args);

// ...

// register offloader with processing service for certain offload type (in this sample it's ItemProcessorType and OffloadItemType)
builder.Services.AddOffload<OffloadItemType, OffloadItemProcessorType>(options =>
{
    options
        // specify error handler, e.g. logger (optional)
        .UseErrorLogger((logger, item, ex) => logger.LogError(ex, "Log in any format {WithValues}", item.Value))
        
        // specify degree of parallelism (optional, default is 1)
        .UseDegreeOfParallelism(2);
});

// ...
    
var app = builder.Build();

// use offloader in your handlers or in your controllers
app.MapPost(
    "/my-heavy-endpoint", 
    async (string name, string avatarUrl, IOffloader<OffloadItemType> offloader) =>
    {
        // do some work before returning response to the client
        
        // offload heavy work to background service~~~~
        await offloader.OffloadAsync(new OffloadItemType(itemValue)); 

        return Results.Created();
    });

// Create a processor that implements IOffloadItemProcessor<OffloadItemType>
public class OffloadItemProcessorType : IOffloadItemProcessor<OffloadItemType>
{
    // Inject any required service, for each item separate scope is created
    private readonly IHttpClientFactory _http;
    private static int _counter;

    public AvatarOffloadItemProcessor(IHttpClientFactory http) => _http = http;

    // Process item here
    public async Task ProcessAsync(ProcessNewAvatar item, CancellationToken ct)
    {
        // process items~~~~
        Interlocked.Increment(ref _counter);
        Console.WriteLine($"{_counter} | Processing user {item.Name} with avatar {item.AvatarUrl}");
        await Task.Delay(Random.Shared.Next(0, 4000), ct);
        Interlocked.Decrement(ref _counter);
        Console.WriteLine($"{_counter}");
    }
}

public record OffloadItemType(string Value);

Downloads by Version

Similar & Alternative Packages

Hand-picked matching NuGet libraries based on tags & ecosystem

Depended On By

Find packages that declare this package as a dependency. NuGet does not provide a reverse-dependency endpoint, so candidates are checked on demand.

Adoption Assessment

Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.

No SPDX license expression is published. Review the license text and your organisation's policy before redistribution.Compatibility metadata lists 1 target framework; confirm the exact target used by your application.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.