WJb 0.108.0

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

⚡ WJb — Explicit Job Execution Engine

If you can’t explain why a job runs, you don’t control your system.


🚀 Quick Start

This is the fastest way to understand how WJb works.

Workflow

send-email → log → done

Code

using WJb;

var store = new InMemoryStore();

var wjb = WJbBuilder.Create(store, cfg =>
{
    cfg.AddAction<SendEmailAction>(Actions.SendEmail);
    cfg.AddAction<LogAction>(Actions.Log);

    cfg.AddService(new SmtpSettings
    {
        Host = "smtp.local"
    });
});

await wjb.EnqueueAsync(Actions.SendEmail, new EmailInput
    {
        To = "user@test.com"
    });

await wjb.ExecuteLoopAsync();

public static class Actions
{
    public const string SendEmail = "send-email";
    public const string Log = "log";
}

public sealed class SendEmailAction(SmtpSettings smtp) : JobAction<EmailInput>
{
    private readonly SmtpSettings _smtp = smtp;

    public override Task<ActionResult> ExecuteAsync(EmailInput input, CancellationToken ct)
    {
        Console.WriteLine($"Email sent to {input.To} via {_smtp.Host}");

        return Task.FromResult(ActionResults.Next(
            new JobCommand(
                Actions.Log,
                new LogInput { Message = $"Email sent to {input.To}" })));
    }
}

public sealed class LogAction : JobAction<LogInput>
{
    public override Task<ActionResult> ExecuteAsync(LogInput input, CancellationToken ct)
    {
        Console.WriteLine(input.Message);

        return Task.FromResult(ActionResults.None());
    }
}

public sealed class EmailInput
{
    public string? To { get; set; }
}

public sealed class LogInput
{
    public string? Message { get; set; }
}

public sealed class SmtpSettings
{
    public string Host { get; set; } = default!;
}

What this demonstrates

  • Actions contain business logic
  • Actions support constructor injection
  • Actions define the workflow
  • Services are resolved automatically
  • Jobs execute through a store-backed runtime
  • Workflows are explicit and deterministic

💥 The Problem

Many background job systems eventually become:

Job
 ↓
Retry
 ↓
Pipeline
 ↓
Middleware
 ↓
???

Soon it becomes difficult to answer:

  • Why did this job run?
  • Why was it retried?
  • Who scheduled the next step?
  • What happens under the hood?

✅ The WJb Way

Job
 ↓
Executor
 ↓
Action
 ↓
ActionResult
 ↓
JobCommand
  • Explicit execution
  • Explicit workflows
  • Explicit retries
  • No hidden pipelines
  • No hidden orchestration
  • No magic

🧠 Core Concepts

Action

An action contains business logic.

public sealed class SendEmailAction : JobAction<SendEmailInput>
{
    public const string Key = "send-email";

    public override Task<ActionResult> ExecuteAsync(SendEmailInput input, CancellationToken ct)
    {
        Console.WriteLine($"Sending email to {input.To}");

        return Task.FromResult(ActionResults.None());
    }
}

ActionResult

Defines what happens next.

Nothing else:

return ActionResults.None();

Schedule another job:

return ActionResults.Next(
    new JobCommand(
        LogAction.Key,
        new LogInput { Message = "Email sent" }));

JobCommand

Represents the next step.

new JobCommand(
    LogAction.Key,
    new LogInput { Message = "Done" })

No hidden workflow engine.

The action decides what happens next.


🏭 Factory

Actions can be created explicitly.

var wjb = WJbBuilder.Create(cfg =>
{
    cfg.AddAction<LogAction>(LogAction.Key);
});

var action = await wjb.CreateAsync(LogAction.Key);

await action.ExecuteAsync(new LogInput
    {
        Message = "Hello WJb 👋"
    },
    CancellationToken.None);

WJb uses explicit action keys, making workflows predictable and easy to trace.


📦 Configuring Services

Actions support constructor injection.

var wjb = WJbBuilder.Create(cfg =>
{
    cfg.AddService(new SmtpSettings
    {
        Host = "localhost"
    });

    cfg.AddAction<ConfiguredEmailAction>(ConfiguredEmailAction.Key);
});
public sealed class ConfiguredEmailAction(SmtpSettings settings) : JobAction<SendEmailInput>
{
    public const string Key = "configured-email";

    public override Task<ActionResult> ExecuteAsync(SendEmailInput input, CancellationToken ct)
    {
        Console.WriteLine(settings.Host);

        return Task.FromResult(ActionResults.None());
    }
}

⚙️ Background Job Execution

Configure WJb:

var store = new InMemoryStore();

var wjb = WJbBuilder.Create(store, cfg =>
{
    cfg.AddAction<SendEmailAction>(SendEmailAction.Key);
});

Queue a job:

await wjb.EnqueueAsync(
    SendEmailAction.Key,
    new SendEmailInput { To = "test@test.com" });

Execute a single cycle:

await wjb.ExecuteOnceAsync();

Or execute continuously:

await wjb.ExecuteLoopAsync();

🔗 Workflows

Actions explicitly define the next step.

public sealed class AAction : JobAction<EmptyInput>
{
    public const string Key = "a";

    public override Task<ActionResult> ExecuteAsync(EmptyInput input, CancellationToken ct)
    {
        return Task.FromResult(ActionResults.Next(
            new JobCommand(BAction.Key, new EmptyInput())));
    }
}

public sealed class BAction : JobAction<EmptyInput>
{
    public const string Key = "b";

    public override Task<ActionResult> ExecuteAsync(EmptyInput input, CancellationToken ct)
    {
        return Task.FromResult(ActionResults.None());
    }
}
A → B

No separate workflow engine required.


🔄 Retries

Retries are configured per job.

await wjb.EnqueueAsync(
    RetryAction.Key,
    new EmptyInput(),
    new JobOptions
    {
        MaxRetries = 3,
        RetryDelay = TimeSpan.FromSeconds(10)
    });
Attempt 1
 ↓
Failed
 ↓
Retry Job Created
 ↓
Attempt 2
 ↓
Completed

🆚 Why WJb?

Feature WJb
Explicit workflows
Explicit retries
Constructor injection
Typed actions
Background execution
Hidden pipelines
Hidden orchestration
Magic execution

✅ Use WJb If

You want:

  • predictable execution
  • explicit workflows
  • strongly typed actions
  • business-driven orchestration
  • simple background processing
  • unit-test-friendly design

❌ WJb Is Not For You If

You want:

  • convention-based magic
  • invisible pipelines
  • hidden orchestration
  • workflow definitions hidden in configuration
  • execution that is difficult to trace

⚡ TL;DR

Action       = Business Logic
ActionResult = Outcome
JobCommand   = Next Step
Executor     = Runner
Store        = Persistence

You always know:

  • why a job started
  • what it did
  • what it scheduled next
  • why it was retried

Need Help?

The author provides professional services for:

  • WJb implementation and integration
  • Custom Action development
  • SQL Server setup and optimization
  • Workflow design
  • Architecture reviews
  • Migration from existing schedulers
  • Production support and troubleshooting

Contact:

📧 ukrguru@gmail.com

Please include:

  • Company name
  • Project description
  • Expected workload or requirements

🎁 Support WJb

If WJb changed how you think about background jobs:

👉 https://ko-fi.com/ukrguru


🎉 Early Support Bonus

Any donation before August 1, 2026 qualifies for:

👉 FREE Solo License


WJb exists because background jobs shouldn't be magic.

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.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on WJb:

Package Downloads
WJb.UI.Blazor

Free Blazor monitoring and administration UI for WJb. Includes jobs monitoring, actions management, services configuration, payload inspection and action testing.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.108.0 50 7/22/2026
Loading failed

WJb 0.108.0
           - Simplified fluent API with IActionFactory, IJobExecutor and IWJb
           - Create(...) and CreateAsync(...) bootstrapping
           - Action-based workflow engine
           - Action chaining through ActionResult.Next(...)
           - Typed inputs and automatic payload binding
           - Constructor-based dependency injection
           - Definition store for actions and services
           - Store-driven configuration with AddActionAsync(...) and AddServiceAsync(...)
           - Job scheduling and delayed execution
           - Retry support and dead-letter handling
           - Progress reporting and job cancellation
           - In-memory and SQL-based storage providers
           - JobsTab monitoring UI component
           - Deterministic and unit-test-friendly architecture
           - Improved API surface and project structure