Rystem.BackgroundJob 10.0.7

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

What is Rystem?

Rystem.BackgroundJob

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:

  • recurring maintenance jobs
  • polling and synchronization tasks
  • cleanup or archival work
  • lightweight scheduled automation inside ASP.NET Core or generic host applications

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.

Resources

Installation

dotnet add package Rystem.BackgroundJob

The current 10.x package targets net10.0 and builds on top of:

Package Architecture

The 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:

  • register a job with AddBackgroundJob<TJob>(...)
  • the package adds the job as a transient service
  • warm-up starts the schedule after the provider is built
  • the manager computes the next CRON occurrence and arms a timer
  • each tick executes the job and schedules the next one

Table of Contents


Implement a Job

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;
    }
}

IBackgroundJob contract

The interface is intentionally small:

public interface IBackgroundJob
{
    Task ActionToDoAsync(CancellationToken cancellationToken = default);
    Task OnException(Exception exception);
}
  • ActionToDoAsync(...) contains the recurring work
  • OnException(...) is called when ActionToDoAsync(...) throws

If you want the scheduler loop to keep going, OnException(...) should usually log or handle the exception rather than rethrow it.

Dependency injection behavior

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:

  • singleton dependencies stay stable across executions
  • scoped dependencies are shared inside one execution but change across later executions
  • transient dependencies are recreated even inside the same execution when resolved twice

That makes constructor injection work the same way it does in the rest of the application.


Register a Job

Register the job during service setup:

builder.Services.AddBackgroundJob<ReportJob>(options =>
{
    options.Cron = "*/5 * * * *";
    options.RunImmediately = true;
});

What AddBackgroundJob<TJob>(...) does internally:

  • registers the default IBackgroundJobManager with TryAddSingleton(...)
  • enables the Rystem lock service with AddLock()
  • registers TJob as transient
  • creates effective options
  • schedules startup through AddWarmUp(...)

So this package depends on the same warm-up lifecycle documented in src/Core/Rystem.DependencyInjection/README.md.


BackgroundJobOptions

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.


Scheduling and Warm-up

Warm-up starts the scheduler

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.

RunImmediately

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.

CRON format

The package uses Cronos and supports both standard and second-based expressions. The manager detects the format by counting space-separated fields.

*/1 * * * *
  • 5 fields → standard CRON format
  • 6 fields → CRON format with seconds

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.


Multiple Registrations of the Same Job Type

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.


Custom Job Manager

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.


Repository Examples

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 Compatible and additional computed target framework versions.
.NET 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 (1)

Showing the top 1 NuGet packages that depend on Rystem.BackgroundJob:

Package Downloads
Rystem.Queue

Rystem.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.7 60 3/26/2026
10.0.6 148,373 3/3/2026
10.0.5 168 2/22/2026
10.0.4 182 2/9/2026
10.0.3 147,979 1/28/2026
10.0.1 209,148 11/12/2025
9.1.3 363 9/2/2025
9.1.2 764,565 5/29/2025
9.1.1 97,866 5/2/2025
9.0.32 186,720 4/15/2025
9.0.31 5,851 4/2/2025
9.0.30 88,882 3/26/2025
9.0.29 9,031 3/18/2025
9.0.28 289 3/17/2025
9.0.27 295 3/16/2025
9.0.26 284 3/13/2025
9.0.25 52,174 3/9/2025
9.0.21 364 3/6/2025
9.0.20 19,619 3/6/2025
9.0.19 339 3/6/2025
Loading failed