Soenneker.Flywheel.Core
4.0.21
Prefix Reserved
See the version list below for details.
dotnet add package Soenneker.Flywheel.Core --version 4.0.21
NuGet\Install-Package Soenneker.Flywheel.Core -Version 4.0.21
<PackageReference Include="Soenneker.Flywheel.Core" Version="4.0.21" />
<PackageVersion Include="Soenneker.Flywheel.Core" Version="4.0.21" />
<PackageReference Include="Soenneker.Flywheel.Core" />
paket add Soenneker.Flywheel.Core --version 4.0.21
#r "nuget: Soenneker.Flywheel.Core, 4.0.21"
#:package Soenneker.Flywheel.Core@4.0.21
#addin nuget:?package=Soenneker.Flywheel.Core&version=4.0.21
#tool nuget:?package=Soenneker.Flywheel.Core&version=4.0.21
Soenneker.Flywheel.Core
Background jobs for .NET with Redis storage, retries, scheduling, and job chains.
Background work. Everything in view.
Queue a typed job, coordinate workers across instances, and follow each execution in the live Blazor dashboard.
The optional Flywheel Dashboard, shown with illustrative demo data.
Explore Flywheel · Dashboard · Run the demo
Setup
Requires .NET 10 and Redis 6.0.9 or newer. Add these packages to your app:
dotnet add package Soenneker.Flywheel.Core
dotnet add package Soenneker.Flywheel.Redis
dotnet add package Soenneker.Flywheel.Generators
Add PrivateAssets="all" to the Generators package reference in your project file.
Usage
In an ASP.NET Core app's Program.cs, register Flywheel and enqueue a job:
using Soenneker.Flywheel.Core.Attributes;
using Soenneker.Flywheel.Core.Registrars;
using Soenneker.Flywheel.Core.Services.Abstract;
using Soenneker.Flywheel.Generated;
using Soenneker.Flywheel.Redis;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFlywheel(options => options.Workers = 4)
.AddRedis(options => options.ConnectionString = "localhost:6379")
.AddGeneratedJobs();
var app = builder.Build();
var client = app.Services.GetRequiredService<IJobClient>();
await client.Enqueue(FlywheelJobs.MessageJobs_Write,
new Message("Hello from Flywheel"));
await app.RunAsync();
public sealed record Message(string Text);
public sealed class MessageJobs(ILogger<MessageJobs> logger)
{
[FlywheelJob("message.write.v1")]
public Task Write(Message message, CancellationToken cancellationToken)
{
logger.LogInformation("{Message}", message.Text);
return Task.CompletedTask;
}
}
When hosting the optional dashboard API, register its credentials with AddDashboard, map controllers for its HTTP endpoints, and let Flywheel map its authenticated real-time endpoint:
builder.Services.AddFlywheel()
.AddRedis(options => options.ConnectionString = "localhost:6379")
.AddDashboard(options =>
{
options.Username = builder.Configuration["Flywheel:Dashboard:Username"] ?? "";
options.PasswordPhc = builder.Configuration["Flywheel:Dashboard:PasswordPhc"] ?? "";
options.AllowedOrigins = ["https://localhost:7004"];
});
var app = builder.Build();
app.UseRouting();
app.UseFlywheelDashboard();
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimiter();
app.MapControllers();
app.MapFlywheelDashboard();
AllowedOrigins contains additional browser origins that may access the dashboard,
including their scheme and port. An empty list allows same-origin browsers only.
Wildcards, credentials, paths, queries, and fragments are rejected at registration.
Call UseFlywheelDashboard() after routing and trusted forwarded-header processing,
before authentication and authorization. It applies credentialed CORS and rejects
unlisted Origin headers with HTTP 403, including SignalR WebSocket handshakes;
CORS alone does not restrict WebSockets.
Other application endpoints are unaffected.
This controls browser origins, not client IP addresses. Requests without Origin
remain subject to dashboard authentication and antiforgery checks. Keep authentication
enabled; use network access controls if the server itself must be unreachable from
other clients. Dashboard cookies remain Secure and SameSite=Strict, so direct cookie
authentication requires HTTPS and same-site frontend/backend addresses (different
localhost ports or subdomains of the same site work).
Enqueue returns after saving the job. Workers run it in the background. In other services, inject IJobClient to submit jobs.
Handlers can inject IJobProgress and publish durable progress while they run:
public sealed class ImportJobs(IJobProgress progress)
{
[FlywheelJob("contacts.import.v1")]
public async Task Import(ImportRequest request, CancellationToken cancellationToken)
{
for (var i = 0; i < request.Contacts.Count; i++)
{
await Import(request.Contacts[i], cancellationToken);
await progress.Report((i + 1d) / request.Contacts.Count * 100, $"Imported {i + 1} contacts", cancellationToken);
}
}
}
Declare distributed execution limits with the job so every server applies the same generated policy before workers start:
[FlywheelJob("contacts.import.v1", MaxConcurrency = 1)]
public Task Import(ImportRequest request, CancellationToken cancellationToken) => // ...
IJobClient.ConfigureMethod remains available for limits that genuinely need to change at runtime.
Queue and schedule jobs
Use IJobClient for immediate, delayed, scheduled, recurring, cron, or chained jobs:
await client.Enqueue(FlywheelJobs.MessageJobs_Write, new Message("Later"),
delay: TimeSpan.FromMinutes(10));
await client.Recurring("hourly-message", FlywheelJobs.MessageJobs_Write,
new Message("Every hour"), TimeSpan.FromHours(1));
await client.Schedule("weekday-message", FlywheelJobs.MessageJobs_Write,
new Message("Good morning"), "0 9 * * MON-FRI",
timeZoneId: "America/Chicago");
See IJobClient for the full API.
Jobs can run more than once; make handlers safe to retry and pass cancellation tokens to async work. Keep job names stable. Reusing a recurring or cron schedule ID leaves the existing schedule unchanged.
Related
- Redis: storage configuration.
- Generators: job method requirements.
- Dashboard: view jobs and logs.
- Demo: run a complete app.
| 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
- Soenneker.Asyncs.Locks (>= 4.0.79)
- Soenneker.Atomics.ValueInts (>= 4.0.26)
- Soenneker.Cron.Parser (>= 4.0.2)
- Soenneker.Gen.EnumValues (>= 4.0.48)
- Soenneker.Hashing.Pbkdf2 (>= 4.0.36)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Soenneker.Flywheel.Core:
| Package | Downloads |
|---|---|
|
Soenneker.Flywheel.Redis
Redis-backed storage and distributed coordination for Soenneker Flywheel. |
|
|
Soenneker.Flywheel.Filesystem
Filesystem storage using Librarian for Soenneker Flywheel. |
|
|
Soenneker.Flywheel.Memory
In-process memory storage and coordination for Soenneker Flywheel. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.0.65 | 0 | 9/16/2026 |
| 5.0.64 | 0 | 9/16/2026 |
| 5.0.63 | 0 | 9/16/2026 |
| 5.0.62 | 2 | 9/15/2026 |
| 5.0.61 | 30 | 9/15/2026 |
| 5.0.60 | 33 | 9/15/2026 |
| 5.0.59 | 36 | 9/15/2026 |
| 5.0.58 | 48 | 9/15/2026 |
| 5.0.56 | 43 | 9/14/2026 |
| 5.0.55 | 46 | 9/14/2026 |
| 5.0.53 | 46 | 9/14/2026 |
| 5.0.52 | 47 | 9/14/2026 |
| 5.0.51 | 47 | 9/14/2026 |
| 5.0.50 | 50 | 9/14/2026 |
| 5.0.49 | 46 | 9/14/2026 |
| 5.0.48 | 47 | 9/14/2026 |
| 5.0.47 | 52 | 9/13/2026 |
| 5.0.46 | 51 | 9/13/2026 |
| 5.0.45 | 51 | 9/13/2026 |
| 4.0.21 | 107 | 9/8/2026 |