WJb 0.104.0
dotnet add package WJb --version 0.104.0
NuGet\Install-Package WJb -Version 0.104.0
<PackageReference Include="WJb" Version="0.104.0" />
<PackageVersion Include="WJb" Version="0.104.0" />
<PackageReference Include="WJb" />
paket add WJb --version 0.104.0
#r "nuget: WJb, 0.104.0"
#:package WJb@0.104.0
#addin nuget:?package=WJb&version=0.104.0
#tool nuget:?package=WJb&version=0.104.0
⚡ WJb — Explicit Job Execution Engine
If you can’t explain why a job runs — you don’t control your system.
💥 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>();
await action.ExecuteAsync(
new LogInput
{
Message = "Hello WJb 👋"
});
📦 Configuring Services
Actions can receive dependencies through constructor injection.
var wjb = WJbBuilder.Create(cfg =>
{
cfg.AddSetting(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(cfg =>
{
cfg.AddAction<SendEmailAction>(
SendEmailAction.Key);
cfg.AddSetting(new SmtpSettings
{
Host = "smtp.local"
});
cfg.UseStore(store);
});
Queue a job:
await wjb.Executor!.EnqueueAsync(
SendEmailAction.Key,
new
{
to = "test@test.com"
});
Run a single execution cycle:
await wjb.Executor.ExecuteOnceAsync();
Or run continuously:
await wjb.Executor.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.Executor.EnqueueAsync(
ImportAction.Key,
new ImportInput(),
new JobOptions
{
MaxRetries = 3,
RetryDelay = TimeSpan.FromSeconds(10)
});
🆚 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
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial public release of WJb.
Features:
- Explicit job execution model
- Typed actions and payload binding
- Action chaining via ActionResult and JobCommand
- In-memory job storage
- Job retries and cancellation support
- Constructor-based dependency injection
- Workflow execution engine
- Service registry and action factory
- Lightweight, testable architecture