![]() |
VOOZH | about |
dotnet add package Rystem.BackgroundJob --version 10.0.8
NuGet\Install-Package Rystem.BackgroundJob -Version 10.0.8
<PackageReference Include="Rystem.BackgroundJob" Version="10.0.8" />
<PackageVersion Include="Rystem.BackgroundJob" Version="10.0.8" />Directory.Packages.props
<PackageReference Include="Rystem.BackgroundJob" />Project file
paket add Rystem.BackgroundJob --version 10.0.8
#r "nuget: Rystem.BackgroundJob, 10.0.8"
#:package Rystem.BackgroundJob@10.0.8
#addin nuget:?package=Rystem.BackgroundJob&version=10.0.8Install as a Cake Addin
#tool nuget:?package=Rystem.BackgroundJob&version=10.0.8Install as a Cake Tool
Rystem.BackgroundJob adds CRON-based recurring jobs on top of the Rystem DI stack.
The package is designed for applications that want small recurring jobs without introducing a heavier external scheduler. Jobs are registered through dependency injection, started during DI warm-up, and executed according to standard 5-field or 6-field CRON expressions through Cronos.
It is most useful for:
The best source-backed examples for this package are the package implementation itself plus the sample web app in src/Extensions/BackgroundJob/Test/Rystem.BackgroundJob.WebApp.
dotnet add package Rystem.BackgroundJob
The current 10.x package targets net10.0 and builds on top of:
CronosRystem.ConcurrencyRystem.DependencyInjectionThe package is intentionally compact and revolves around four pieces.
| Piece | Purpose |
|---|---|
IBackgroundJob |
Contract implemented by each scheduled job |
BackgroundJobOptions |
CRON, startup behavior, and logical key |
IBackgroundJobManager |
Scheduler abstraction responsible for running jobs |
AddBackgroundJob<TJob> |
DI entry point that registers the job and wires startup through warm-up |
At a high level, the flow is:
AddBackgroundJob<TJob>(...)Jobs implement IBackgroundJob.
The public job types live in the System.Timers namespace, so the usual starting point is:
using System.Timers;
Then implement the job itself:
using System.Timers;
public sealed class ReportJob : IBackgroundJob
{
private readonly ILogger<ReportJob> _logger;
public ReportJob(ILogger<ReportJob> logger)
{
_logger = logger;
}
public async Task ActionToDoAsync(CancellationToken cancellationToken = default)
{
_logger.LogInformation("Report job running at {Time}", DateTime.UtcNow);
await Task.Delay(10, cancellationToken);
}
public Task OnException(Exception exception)
{
_logger.LogError(exception, "Report job failed");
return Task.CompletedTask;
}
}
The interface is intentionally small:
public interface IBackgroundJob
{
Task ActionToDoAsync(CancellationToken cancellationToken = default);
Task OnException(Exception exception);
}
ActionToDoAsync(...) contains the recurring workOnException(...) is called when ActionToDoAsync(...) throwsIf you want the scheduler loop to keep going, OnException(...) should usually log or handle the exception rather than rethrow it.
AddBackgroundJob<TJob>(...) registers the job as Transient.
The sample app in src/Extensions/BackgroundJob/Test/Rystem.BackgroundJob.WebApp/BackgroundJob.cs shows the intended lifetime behavior clearly:
That makes constructor injection work the same way it does in the rest of the application.
Register the job during service setup:
builder.Services.AddBackgroundJob<ReportJob>(options =>
{
options.Cron = "*/5 * * * *";
options.RunImmediately = true;
});
What AddBackgroundJob<TJob>(...) does internally:
IBackgroundJobManager with TryAddSingleton(...)AddLock()TJob as transientAddWarmUp(...)So this package depends on the same warm-up lifecycle documented in src/Core/Rystem.DependencyInjection/README.md.
BackgroundJobOptions contains the scheduling settings:
public sealed class BackgroundJobOptions
{
public string? Key { get; set; }
public bool RunImmediately { get; set; }
public string Cron { get; set; } = "* * * * *";
}
In practice, AddBackgroundJob<TJob>(...) starts from these effective defaults before your configuration delegate runs:
| Property | Effective default in AddBackgroundJob(...) |
Purpose |
|---|---|---|
Key |
random GUID | Distinguishes one registration from another |
RunImmediately |
false |
Executes once during warm-up before the timer begins |
Cron |
"0 1 * * *" |
Schedules the job daily at 01:00 UTC |
That distinction matters because the raw BackgroundJobOptions class initializes Cron to "* * * * *", while AddBackgroundJob(...) overwrites the starting default to "0 1 * * *" unless you set a different value.
Jobs do not start automatically just because they were registered. They start when warm-up runs.
var app = builder.Build();
await app.Services.WarmUpAsync();
app.Run();
Without WarmUpAsync(), the recurring timers are never started.
If RunImmediately is true, the manager executes ActionToDoAsync(...) once during warm-up before creating the first scheduled timer.
builder.Services.AddBackgroundJob<ReportJob>(options =>
{
options.Cron = "0 */2 * * *";
options.RunImmediately = true;
});
This is useful when the first execution should happen during startup instead of waiting for the first future CRON occurrence.
The package uses Cronos and supports both standard and second-based expressions. The manager detects the format by counting space-separated fields.
*/1 * * * *
Examples:
*/1 * * * *
0 */2 * * *
0 9 * * 1-5
*/30 * * * * *
0 0 9 * * 1-5
For quick validation, crontab.guru is still a convenient companion for the 5-field form.
You can register the same job type more than once.
builder.Services.AddBackgroundJob<ReportJob>(options =>
{
options.Key = "daily";
options.Cron = "0 8 * * *";
});
builder.Services.AddBackgroundJob<ReportJob>(options =>
{
options.Key = "weekly";
options.Cron = "0 8 * * 1";
});
The built-in manager uses a key shaped like:
BackgroundWork_{options.Key}_{jobTypeFullName}
So multiple registrations stay independent as long as they do not collide on both job type and key.
If you do not set Key, the package generates a GUID automatically, which already keeps separate registrations distinct.
If you need different scheduling or execution semantics, replace the default manager.
builder.Services.AddBackgroundJobManager<MyCustomJobManager>();
Your implementation must satisfy:
public interface IBackgroundJobManager
{
Task RunAsync(
IBackgroundJob job,
BackgroundJobOptions options,
Func<IBackgroundJob>? factory = null,
CancellationToken cancellationToken = default);
}
The optional factory parameter is the hook the default manager uses to recreate jobs for later scheduled executions.
Important: call AddBackgroundJobManager<TJobManager>() before any AddBackgroundJob(...) registration, because the built-in manager is registered with TryAddSingleton(...).
The built-in BackgroundJobManager also uses the Rystem lock service to prevent concurrent timer updates for the same logical job key.
The most useful references for this package are:
This README stays focused because Rystem.BackgroundJob is a narrow package: one job contract, one manager abstraction, one DI registration path, and a CRON-driven execution loop.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 1 NuGet packages that depend on Rystem.BackgroundJob:
| Package | Downloads |
|---|---|
|
Rystem.Queue
Rystem. |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.8 | 5,570 | 5/13/2026 |
| 10.0.7 | 186 | 3/26/2026 |
| 10.0.6 | 433,722 | 3/3/2026 |
| 10.0.5 | 202 | 2/22/2026 |
| 10.0.4 | 211 | 2/9/2026 |
| 10.0.3 | 148,010 | 1/28/2026 |
| 10.0.1 | 209,180 | 11/12/2025 |
| 9.1.3 | 387 | 9/2/2025 |
| 9.1.2 | 764,589 | 5/29/2025 |
| 9.1.1 | 97,896 | 5/2/2025 |
| 9.0.32 | 186,749 | 4/15/2025 |
| 9.0.31 | 5,873 | 4/2/2025 |
| 9.0.30 | 88,916 | 3/26/2025 |
| 9.0.29 | 9,056 | 3/18/2025 |
| 9.0.28 | 314 | 3/17/2025 |
| 9.0.27 | 318 | 3/16/2025 |
| 9.0.26 | 307 | 3/13/2025 |
| 9.0.25 | 52,202 | 3/9/2025 |
| 9.0.21 | 385 | 3/6/2025 |
| 9.0.20 | 19,648 | 3/6/2025 |