WorkR 0.3.3
dotnet add package WorkR --version 0.3.3
NuGet\Install-Package WorkR -Version 0.3.3
<PackageReference Include="WorkR" Version="0.3.3" />
<PackageVersion Include="WorkR" Version="0.3.3" />
<PackageReference Include="WorkR" />
paket add WorkR --version 0.3.3
#r "nuget: WorkR, 0.3.3"
#:package WorkR@0.3.3
#addin nuget:?package=WorkR&version=0.3.3
#tool nuget:?package=WorkR&version=0.3.3
WorkR
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 | Versions 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. |
-
net10.0
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- WorkR.Abstractions (>= 0.3.3)
-
net8.0
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- WorkR.Abstractions (>= 0.3.3)
-
net9.0
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.0)
- WorkR.Abstractions (>= 0.3.3)
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 |