NOBS.JobSystem.UI 1.0.7

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

NOBS.JobSystem

A lightweight, persistence-agnostic, dependency-injection friendly job scheduling system for .NET applications. It provides CRON-based scheduling, job chaining, manual triggering, and a persistent execution history with an optional Blazor-based monitoring UI.

Key Features

  • Fluent Configuration: A clean, expressive API for registering jobs and their dependencies.
  • Pluggable Storage: Persist job history to SQL Server, JSON files, or a custom provider. The core system is completely decoupled from the storage layer.
  • Stable Job Identity: Use the [JobName] attribute to assign a persistent identifier to your jobs, preventing history loss when you refactor class names.
  • CRON Scheduling: Define recurring jobs using standard CRON expressions (minute hour day-of-month month day-of-week).
  • Job Chaining: Create powerful workflows by specifying continuation jobs on success or failure, either statically during registration or dynamically at runtime.
  • Global Error Handling: Configure a specific job to run whenever any unhandled exception occurs.
  • DI-First Design: Jobs are resolved from the service container, giving them full access to all registered application services (e.g., database contexts, loggers, business services).
  • Manual Triggering: Force any registered job to run immediately via the monitoring UI or programmatically using the IJobTrigger service.
  • Optional Monitoring UI: A clean, lightweight Blazor UI to monitor job statuses, last run times, and next scheduled runs.

Packages

The system is distributed across multiple NuGet packages for modularity:

Package Description
NOBS.JobSystem The core library containing the job orchestrator and scheduling logic.
NOBS.JobSystem.Hosting A meta-package for easily installing and configuring the UI endpoint.
NOBS.JobSystem.UI The Blazor-based monitoring UI. (Typically consumed via the Hosting package)
NOBS.JobSystem.Stores.SqlServer Persistence provider for Microsoft SQL Server.
NOBS.JobSystem.Stores.JsonFile Persistence provider for a local JSON file (ideal for simple, single-instance apps).

Usage Guide

This guide demonstrates how to set up a multi-step job workflow:

  1. A scheduled job ProcessDailyReportsJob runs daily.
  2. On success, it chains to ArchiveOldReportsJob.
  3. On any failure, it chains to ReportGenerationFailedJob.

1. Install Packages

Install the core library, a storage provider, and the UI hosting package.

dotnet add package NOBS.JobSystem
dotnet add package NOBS.JobSystem.Hosting
dotnet add package NOBS.JobSystem.Stores.JsonFile

2. Define Your Jobs

Jobs are simple classes that implement IJob. They can be injected with any service from your DI container.

ProcessDailyReportsJob.cs

// This job runs on a schedule.
[JobName("report-processor")]
public class ProcessDailyReportsJob(ILogger<ProcessDailyReportsJob> logger, IReportGenerator reportGenerator) : IJob
{
    public async Task<JobExecutionResult> ExecuteAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation("Starting daily report generation...");

        bool success = await reportGenerator.GenerateAsync(cancellationToken);

        if (success)
        {
            // The job succeeded. The static chain defined in Program.cs
            // for success (`ArchiveOldReportsJob`) will be triggered.
            return JobExecutionResult.Success();
        }

        // The job failed. The static chain for failure will be triggered.
        return JobExecutionResult.Failure();
    }
}

ArchiveOldReportsJob.cs

// This job is triggered only as a continuation.
[JobName("report-archiver")]
public class ArchiveOldReportsJob(ILogger<ArchiveOldReportsJob> logger) : IJob
{
    public async Task<JobExecutionResult> ExecuteAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation("Archiving old reports...");
        // ... archiving logic ...
        await Task.Delay(500, cancellationToken);
        return JobExecutionResult.Success();
    }
}

ReportGenerationFailedJob.cs

// This job is an error handler.
[JobName("report-failure-handler")]
public class ReportGenerationFailedJob(ILogger<ReportGenerationFailedJob> logger) : IJob
{
public async Task<JobExecutionResult> ExecuteAsync(CancellationToken cancellationToken)
{
logger.LogError("Report generation failed. Notifying administrators.");
// ... notification logic ...
await Task.CompletedTask;
return JobExecutionResult.Success();
}
}

3. Register Services and UI

In your Program.cs, configure the job system, register your jobs, and map the UI.

using NOBS.JobSystem;
using NOBS.JobSystem.Execution;
using NOBS.JobSystem.Hosting;
using NOBS.JobSystem.Stores.JsonFile;

var builder = WebApplication.CreateBuilder(args);

// Add application services used by your jobs
builder.Services.AddScoped<IReportGenerator, ReportGenerator>();

// 1. Add the Job System
builder.Services
    .AddJobSystem(registry =>
    {
        // 2. Register jobs and define workflows
        registry.AddJob<ProcessDailyReportsJob>("0 1 * * *") // Run at 1:00 AM UTC daily
            .OnSuccess<ArchiveOldReportsJob>()
            .OnError<ReportGenerationFailedJob>();

        // Register continuation/error jobs without a schedule
        registry.AddJob<ArchiveOldReportsJob>();
        registry.AddJob<ReportGenerationFailedJob>();
    })
    // 3. Configure a storage provider
    .UseJsonFile(options =>
    {
        // The path is relative to the application's content root.
        options.FilePath = "Data/job_history.json"; 
        options.PollingFrequency = TimeSpan.FromSeconds(30);
    });
    // Or, for SQL Server:
    // .UseSqlServer(options =>
    // {
    //     options.ConnectionString = builder.Configuration.GetConnectionString("JobDb");
    //     options.SchemaName = "jobs";
    //     options.PollingFrequency = TimeSpan.FromMinutes(1);
    // });

// 4. Add services for the Blazor UI
builder.Services.AddJobMonitorUI();

var app = builder.Build();

// ... other middleware

// 5. Map the Blazor UI and the trigger endpoint
app.MapHostedJobSystemUI();

app.Run();

Advanced Usage

Dynamic Job Chaining

Instead of defining the entire chain at registration, a job can dynamically decide which job to run next based on its internal logic.

public class DynamicWorkflowJob : IJob
{
    public async Task<JobExecutionResult> ExecuteAsync(CancellationToken cancellationToken)
    {
        var condition = await CheckSomeConditionAsync();

        if (condition)
        {
            // Dynamically chain to HighPriorityContinuationJob
            return JobExecutionResult.Success(typeof(HighPriorityContinuationJob));
        }

        // Dynamically chain to LowPriorityContinuationJob
        return JobExecutionResult.Success(typeof(LowPriorityContinuationJob));
    }
}

Note: HighPriorityContinuationJob and LowPriorityContinuationJob must still be registered with registry.AddJob<T>() so the system is aware of them.

Stable Job Naming with [JobName]

Class names and namespaces can change during refactoring. To ensure the job's execution history is preserved, assign a stable, unique name with the [JobName] attribute. This name is used as the key in the persistence store, decoupling the job's identity from its type name.

[JobName("process-customer-invoices-v1")]
public class ProcessInvoicesJob : IJob
{
    // ...
}

Programmatic Triggering

Besides the UI, you can trigger a job from anywhere in your application by injecting IJobTrigger.

public class SomeApiController(IJobTrigger jobTrigger) : ControllerBase
{
    [HttpPost("invoices/process-now")]
    public async Task<IActionResult> ProcessInvoicesNow()
    {
        // Use the stable name defined in the [JobName] attribute
        await jobTrigger.TriggerJobAsync("process-customer-invoices-v1");
        return Accepted();
    }
}

Building From Source

To build the project from source, clone the repository and run the .NET build command from the root directory.

git clone https://github.com/scippo97sensibleproductions/NOBS.JobSystem.git
cd NOBS.JobSystem
dotnet build -c Release

Contributing

Contributions are welcome. Please follow these standard steps:

  1. Fork the repository.
  2. Create a new feature branch (git checkout -b feature/your-feature-name).
  3. Commit your changes (git commit -m 'Add some feature').
  4. Push to the branch (git push origin feature/your-feature-name).
  5. Open a pull request.

License

This project is licensed under the MIT License. See the LICENSE file for details.

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 NOBS.JobSystem.UI:

Package Downloads
NOBS.JobSystem.Hosting

A meta-package to easily install and configure the NOBS.JobSystem core library and its Blazor UI.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.7 136 9/25/2025
1.0.6 122 9/25/2025
1.0.5 132 9/25/2025
1.0.4 126 9/25/2025
1.0.1 135 9/24/2025
1.0.0 133 9/24/2025