WJb 0.107.0
See the version list below for details.
dotnet add package WJb --version 0.107.0
NuGet\Install-Package WJb -Version 0.107.0
<PackageReference Include="WJb" Version="0.107.0" />
<PackageVersion Include="WJb" Version="0.107.0" />
<PackageReference Include="WJb" />
paket add WJb --version 0.107.0
#r "nuget: WJb, 0.107.0"
#:package WJb@0.107.0
#addin nuget:?package=WJb&version=0.107.0
#tool nuget:?package=WJb&version=0.107.0
⚡ 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:
🎉 Early Support Bonus
Any donation before August 1, 2026 qualifies for:
👉 FREE Solo License
WJb exists because background jobs shouldn't be magic.
| Product | Versions 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. |
-
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.
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