WJb 0.107.0

There is a newer version of this package available.
See the version list below for details.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package WJb --version 0.107.0
                    
NuGet\Install-Package WJb -Version 0.107.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.107.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WJb" Version="0.107.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.107.0
                    
#r "nuget: WJb, 0.107.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.107.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.107.0
                    
Install as a Cake Addin
#tool nuget:?package=WJb&version=0.107.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

Run

dotnet run

Code

using WJb;

Console.OutputEncoding = System.Text.Encoding.UTF8;

Console.WriteLine("=== WJb Quick Start ===\n");

Console.WriteLine($"""
Workflow:
{Actions.SendEmail} → {Actions.Log} → done

""");

var store = new InMemoryStore();

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

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

// Enqueue first job
Console.WriteLine($"[App] Enqueue: {Actions.SendEmail}");

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

// Execute all pending jobs
Console.WriteLine("[App] Start execution...\n");

await wjb.ExecuteLoopAsync();

Console.WriteLine("\n=== Completed ===");

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(
            $"[Action] {Actions.SendEmail} → {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(
            $"[Action] {Actions.Log} → {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!;
}

Output

=== WJb Quick Start ===

Workflow:
send-email → log → done

[App] Enqueue: send-email
[App] Start execution...

[Action] send-email → user@test.com via smtp.local
[Action] log → Email sent to user@test.com

=== Completed ===

What this demonstrates

  • Actions contain business logic
  • Actions can use dependency injection
  • Services are resolved automatically
  • Each action explicitly defines the next step
  • Workflows are deterministic and visible
  • Jobs execute through a store-backed runtime

👉 You always know what happens and why.


💥 The Problem

Most job systems eventually become:

Job → retry → pipeline → middleware → ???

Questions become difficult to answer:

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

✅ The WJb Way

Job → Executor → Action → ActionResult → Next Jobs

✔ Explicit execution

✔ Explicit workflow

✔ Explicit retries

✔ No hidden pipelines

✔ No magic


🧠 Core Concepts

Action

Your 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.

return ActionResults.None();

or

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

JobCommand

Explicitly schedules the next job.

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

No hidden orchestration.

The action decides the workflow.


🚀 Minimal Start

public sealed class LogAction : JobAction<LogInput>
{
    public const string Key = "log";

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

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

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. Actions are registered and resolved by key, keeping workflows predictable and transparent.


📦 Configuring Services

Actions can receive dependencies through constructor injection.

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

    cfg.AddAction<SendEmailAction>(
        SendEmailAction.Key);
});

Action:

public sealed class SendEmailAction(
    SmtpSettings settings)
    : JobAction<SendEmailInput>
{
    public const string Key = "send-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);

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

Queue a job:

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

Run a single execution cycle:

await wjb.ExecuteOnceAsync();

Or run 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 → C

No workflow engine required.


🔄 Retries

Retries are configured per job.

await wjb.EnqueueAsync(
    ImportAction.Key,
    new ImportInput(),
    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 process orchestration
  • simple, testable background jobs

❌ WJb Is Not For You If

You want:

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

⚡ TL;DR

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

You always know:

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

🎁 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 48 7/22/2026
Loading failed

WJb 0.107.0
           - Simplified fluent API with Create(...) and CreateAsync(...)
           - Action-based workflow engine
           - Action chaining through IAction.NextAsync(...)
           - Typed inputs and payload binding
           - Constructor-based dependency injection
           - Job scheduling and delayed execution
           - Retry support
           - Progress reporting
           - Job cancellation
           - In-memory job storage
           - IWJbExecutor execution engine
           - JobsTab monitoring UI component
           - Deterministic and unit-test-friendly architecture
           - Improved API surface and project structure