WorkR 0.3.3

dotnet add package WorkR --version 0.3.3
                    
NuGet\Install-Package WorkR -Version 0.3.3
                    
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="WorkR" Version="0.3.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WorkR" Version="0.3.3" />
                    
Directory.Packages.props
<PackageReference Include="WorkR" />
                    
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 WorkR --version 0.3.3
                    
#r "nuget: WorkR, 0.3.3"
                    
#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 WorkR@0.3.3
                    
#: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=WorkR&version=0.3.3
                    
Install as a Cake Addin
#tool nuget:?package=WorkR&version=0.3.3
                    
Install as a Cake Tool

WorkR

.NET NuGet License: MIT

A lightweight .NET background worker framework built on top of IHostedService. Define workers and triggers, compose middleware, and let WorkR manage the execution loop.


Installation

dotnet add package WorkR

Getting Started

Define a Worker

public class MyWorker : IWorker<EmptyTriggerContext>
{
    private readonly ILogger<MyWorker> _logger;

    public MyWorker(ILogger<MyWorker> logger) => _logger = logger;

    public Task ExecuteAsync(EmptyTriggerContext context, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Running at {timestamp}", context.OccurredAt);
        return Task.CompletedTask;
    }
}

Register a Worker

// Fire exactly once when the host starts
builder.Services.AddRunOnceWorker<MyWorker>();

// Full control over the pipeline
builder.Services.AddRunOnceWorker(pipeline =>
    pipeline.AddWorker<MyWorker>());

AddRunOnceWorker

Fires the pipeline exactly once when the host starts. Useful for startup tasks, migrations, or one-off initialisation.

builder.Services.AddRunOnceWorker<MyStartupWorker>();

Default middleware: UseScope. This is applied to the first worker in the chain only — workers in a chain inherit the scope but have no additional middleware unless configured explicitly.


Chained Workers

Workers can be chained using IWorker<TIn, TOut>. Each worker transforms the value and calls next to pass it to the next step.

public class FetchWorker : IWorker<EmptyTriggerContext, IReadOnlyList<Order>>
{
    private readonly IOrderRepository _repo;

    public FetchWorker(IOrderRepository repo) => _repo = repo;

    public async Task ExecuteAsync(
        EmptyTriggerContext context,
        WorkerDelegate<IReadOnlyList<Order>> next,
        CancellationToken cancellationToken)
    {
        var orders = await _repo.GetPendingAsync(cancellationToken);
        await next(orders, cancellationToken);
    }
}

public class ProcessWorker : IWorker<IReadOnlyList<Order>>
{
    public async Task ExecuteAsync(IReadOnlyList<Order> orders, CancellationToken cancellationToken)
    {
        foreach (var order in orders)
        {
            // process each order
        }
    }
}

Register the chain using AddWorker:

builder.Services.AddRunOnceWorker(pipeline =>
    pipeline
        .AddWorker<FetchWorker, IReadOnlyList<Order>>()
        .AddWorker<ProcessWorker>());

Custom Triggers

Implement ITrigger<TContext> to define a trigger with any execution model, then register it with AddWorker:

public class MyTrigger : ITrigger<EmptyTriggerContext>
{
    public async Task ExecuteAsync(WorkerDelegate<EmptyTriggerContext> workerPipeline, CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await workerPipeline(new EmptyTriggerContext(DateTimeOffset.UtcNow), stoppingToken);
            await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
        }
    }
}

builder.Services.AddWorker<MyTrigger, EmptyTriggerContext>(
    sp => ActivatorUtilities.CreateInstance<MyTrigger>(sp),
    pipeline => pipeline.AddWorker<MyWorker>());

Worker Lifetime

Workers are registered with the DI container automatically. The default lifetime is Transient:

// Override lifetime
pipeline.AddWorker<MyWorker>(ServiceLifetime.Scoped)

// Skip registration (if already registered elsewhere)
pipeline.AddWorker<MyWorker>(lifetime: null)

Middleware

Middleware is configured per worker step using MiddlewarePipelineBuilder. It wraps worker execution and is applied in registration order (outermost first).

UseScope

Creates a new IServiceScope per execution. Workers downstream of UseScope resolve their dependencies from the scoped container.

middleware.UseScope()

UseErrorHandling

Catches and swallows exceptions to prevent a failing execution from crashing the pipeline. An optional predicate filters which exceptions are handled.

middleware.UseErrorHandling<Exception>()
middleware.UseErrorHandling<HttpRequestException>()
middleware.UseErrorHandling<HttpRequestException>(ex => ex.StatusCode == HttpStatusCode.ServiceUnavailable)

UseTimeout

Cancels execution if it exceeds the specified duration.

middleware.UseTimeout(TimeSpan.FromSeconds(30))

Custom Middleware

Implement IWorkerMiddleware to create reusable cross-cutting behaviour:

public class TracingMiddleware : IWorkerMiddleware
{
    public async Task ExecuteAsync(Func<CancellationToken, Task> next, CancellationToken cancellationToken)
    {
        using var activity = Activity.StartActivity("worker.execute");
        await next(cancellationToken);
    }
}

Register it with UseMiddleware:

// Resolved via factory (access to DI)
middleware.UseMiddleware(sp => new TracingMiddleware(sp.GetRequiredService<ITracer>()))

// Pre-constructed instance
middleware.UseMiddleware(new TracingMiddleware())

Middleware Ordering

A typical ordering:

middleware
    .UseScope()                            // create DI scope for this execution
    .UseErrorHandling<Exception>()         // catch any exceptions from this or subsequent workers
    .UseTimeout(TimeSpan.FromSeconds(30))  // cancel if too slow

Middleware can be configured per step in a chain and effects all subsequent steps:

builder.Services.AddRunOnceWorker(pipeline =>
    pipeline
        .AddWorker<FetchWorker, IReadOnlyList<Order>>(middleware: mw => mw.UseTimeout(TimeSpan.FromSeconds(10)))
        .AddWorker<ProcessWorker>());
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  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 is compatible.  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.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on WorkR:

Package Downloads
WorkR.Triggers.Timers

Timer-based triggers for WorkR, supporting delay-based and cron-scheduled background workers.

WorkR.Triggers.AzureServiceBus

Azure Service Bus trigger for WorkR.

WorkR.Triggers.AzureStorageQueues

Azure Storage Queue trigger for WorkR.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.3 48 5/28/2026
0.3.2-preview 49 5/28/2026
0.2.1 157 5/22/2026
0.1.7-g2e3259f211 149 5/19/2026
0.1.4 153 5/22/2026
0.1.2-g00a231d74d 104 5/18/2026
0.1.1 106 5/18/2026
0.0.17-g69ea0bb7c1 109 5/18/2026
0.0.6-preview 50 5/28/2026
0.0.5-preview 46 5/28/2026
0.0.3-ga2a8ae0176 108 5/15/2026
0.0.3-g6695740d51 107 5/15/2026