Atomizer.EntityFrameworkCore
0.6.2
See the version list below for details.
dotnet add package Atomizer.EntityFrameworkCore --version 0.6.2
NuGet\Install-Package Atomizer.EntityFrameworkCore -Version 0.6.2
<PackageReference Include="Atomizer.EntityFrameworkCore" Version="0.6.2" />
<PackageVersion Include="Atomizer.EntityFrameworkCore" Version="0.6.2" />
<PackageReference Include="Atomizer.EntityFrameworkCore" />
paket add Atomizer.EntityFrameworkCore --version 0.6.2
#r "nuget: Atomizer.EntityFrameworkCore, 0.6.2"
#:package Atomizer.EntityFrameworkCore@0.6.2
#addin nuget:?package=Atomizer.EntityFrameworkCore&version=0.6.2
#tool nuget:?package=Atomizer.EntityFrameworkCore&version=0.6.2
Atomizer
Break down complexity. Scale with confidence. Atomizer transforms large-scale job processing into atomic, reliable, and easily managed unitsβmaking distributed systems simple, robust, and a joy to work with.
Overview
Atomizer is a modern, high-performance job scheduling and queueing framework for ASP.NET Core. Built for cloud-native, distributed applications as well as smaller setups and local development, Atomizer provides a powerful yet easy-to-use solution for processing background jobs, handling complex workflows, and scaling your applications effortlessly.
- Effortless distributed scaling: Atomizer works seamlessly in clustered setups, letting you process jobs across multiple servers for true horizontal scalability.
- Flexible architecture: Plug in your preferred storage backend, configure multiple queues, and extend with custom drivers or handlers.
- Reliable and robust: Enjoy graceful shutdowns, automatic retries, and job visibility timeouts to ensure jobs are never lost or duplicated.
- Developer-friendly: Atomizer integrates with ASP.NET Core DI, logging, and modern C# features, so you can focus on your business logic.
Features
- β° Recurring Scheduled Jobs β Cron-like recurring execution for time-based workflows.
- π Distributed Processing β Scale out to as many servers as your storage backend supports; Atomizer coordinates job execution across the cluster.
- ποΈ Multiple Storage Backends β Use Entity Framework Core for durable, database-backed queues; Redis for distributed low-latency queues; in-memory for fast local development & testing.
- π Multiple Queues β Configure independent queues with custom processing options for each workload.
- π§© Extensible Drivers & Handlers β Easily add new storage drivers or job handlers; auto-register handlers from assemblies.
- β»οΈ Advanced Retry Policies β Automatic, configurable retries to keep your jobs running smoothlyβeven when things go wrong.
- π Graceful Shutdown β Ensure in-flight jobs finish and pending batched jobs are safely released for re-processing during shutdowns.
- π¦ Batch Processing β Tune throughput with batch size and parallelism settings per queue.
- β³ Visibility Timeout β Prevent job duplication by locking jobs during processing.
- π FIFO Partitioned Processing β Guarantee strict in-order, one-at-a-time execution per partition key (e.g. per customer, per entity).
- π§ͺ In-Memory Driver β Perfect for local development and testing; spin up queues instantly with zero setup.
- π Dashboard β Optional operational dashboard for jobs, schedules, queue statistics, worker heartbeats, and built-in job/schedule actions.
- π ASP.NET Core Integration β Works with DI, logging, and modern C# idioms.
The dashboard gives teams a focused operational view of Atomizer: inspect queue health, review job and schedule details, watch active workers, and trigger common actions without building a custom admin UI.
Quick Start
Get up and running in minutes:
1. Install the package
dotnet add package Atomizer
# Choose a durable storage backend
dotnet add package Atomizer.EntityFrameworkCore
# or
dotnet add package Atomizer.Redis
# Optional: add the monitoring dashboard
dotnet add package Atomizer.Dashboard
2. Configure Atomizer
Set up Atomizer in your ASP.NET Core project:
builder.Services.AddAtomizer(options =>
{
// Configure the default queue
// (optional, a default queue is created automatically with configuration like below)
options.AddQueue(QueueKey.Default, queue =>
{
queue.DegreeOfParallelism = 4; // Max 4 jobs processed concurrently
queue.BatchSize = 10; // Retrieve 10 jobs at a time
queue.VisibilityTimeout = TimeSpan.FromMinutes(5); // Prevent job duplication by "hiding" jobs for 5 minutes while processing
queue.StorageCheckInterval = TimeSpan.FromSeconds(15); // Poll for new jobs every 15 seconds
});
// Add more queues as needed
options.AddQueue("product", queue =>
{
queue.DegreeOfParallelism = 2;
queue.BatchSize = 5;
});
// Register job handlers automatically
options.AddHandlersFrom<AssignStockJobHandler>();
// Use EF Core-backed job storage.
options.UseEntityFrameworkCoreStorage<ExampleDbContext>();
});
// Add Atomizer processing services
builder.Services.AddAtomizerProcessing(options =>
{
options.StartupDelay = TimeSpan.FromSeconds(5); // Delay startup to allow other services to initialize
options.GracefulShutdownTimeout = TimeSpan.FromSeconds(30); // Allow up to 30 seconds for jobs to finish on shutdown
});
// Optional: add the Atomizer Dashboard services
builder.Services.AddAtomizerDashboard(options =>
{
options.Title = "Atomizer Dashboard";
options.StatsRefreshInterval = TimeSpan.FromSeconds(5);
options.JobsRefreshInterval = TimeSpan.FromSeconds(30);
});
Inside your DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.AddAtomizerEntities(schema: "atomizer");
// ...other model config...
}
Make sure to run migrations to create the necessary tables.
Note:
If using MySql, set schema to 'null' or configure schema behavior in your
DbContextas MySql is not compatible with EF Core schemas.
To use Redis-backed storage instead, reference Atomizer.Redis and configure the storage option with a StackExchange.Redis connection string:
using Atomizer.Redis;
builder.Services.AddAtomizer(options =>
{
options.AddHandlersFrom<AssignStockJobHandler>();
options.UseRedisStorage(builder.Configuration.GetConnectionString("Redis")!);
});
Redis storage implements the same IAtomizerStorage monitoring methods as the other backends, so it works with Atomizer.Dashboard without referencing the dashboard package from Atomizer.Redis.
3. Define a Job Handler
Create a handler for your job payload:
public record SendNewsletterCommand(Product Product);
public class SendNewsletterJob(INewsletterService newsletterService, IEmailService emailService)
: IAtomizerJob<SendNewsletterCommand>
{
public async Task HandleAsync(SendNewsletterCommand payload, JobContext context)
{
var subscribers = await newsletterService.GetSubscribersAsync(payload.Product.CategoryId);
var emails = new List<Email>();
foreach (var subscriber in subscribers)
{
emails.Add(new Email { /* ... */ });
}
await Task.WhenAll(emails.ConvertAll(email => emailService.SendEmailAsync(email)));
}
}
4. Enqueue, schedule, execute, or dequeue a Job
Add jobs to the queue from your application code:
app.MapPost(
"/products",
async ([FromServices] IAtomizerClient atomizerClient, [FromServices] ExampleDbContext dbContext) =>
{
var product = new Product { /* ... */, CategoryId = Guid.NewGuid() };
dbContext.Products.Add(product);
await dbContext.SaveChangesAsync();
await atomizerClient.EnqueueAsync(new SendNewsletterCommand(product));
return Results.Created($"/products/{product.Id}", product);
}
);
Use ExecuteAsync when the job should run immediately in the current process but still be recorded in Atomizer as completed or failed:
var jobId = await atomizerClient.ExecuteAsync(new SendNewsletterCommand(product));
If the handler throws, Atomizer records the job as failed and then rethrows the exception to the caller.
Use DequeueAsync to cancel a job that is still pending:
var dequeued = await atomizerClient.DequeueAsync(jobId);
It returns false when the job does not exist or is no longer pending.
5. FIFO Processing (Partitioned Jobs)
To guarantee jobs for the same entity execute one-at-a-time in enqueue order, assign a PartitionKey:
// All stock events for the same product are processed in strict FIFO order.
await atomizerClient.EnqueueAsync(
new StockEvent(productId, "restock", delta: 50),
options => options.PartitionKey = new PartitionKey(productId.ToString())
);
Jobs sharing the same PartitionKey and queue are serialized: the next job in the partition only starts after the previous one completes (or fails and is rescheduled). Unpartitioned jobs in the same queue are unaffected and continue to process in parallel.
6. Schedule Recurring Jobs
in Program.cs:
...
var app = builder.Build();
var atomizer = app.Services.GetRequiredService<IAtomizerClient>();
await atomizer.ScheduleRecurringAsync(
new LoggerJobPayload("Recurring job started", LogLevel.Information),
"LoggerJob",
Schedule.Every(2).Minutes()
);
// Later, when the recurring schedule should stop:
// safe to call even when the schedule has already been deleted or never existed.
await atomizer.DeleteRecurringAsync("LoggerJob");
...
7. Dashboard (Optional)
If you install Atomizer.Dashboard, map the embedded dashboard SPA after building your app:
var app = builder.Build();
app.MapAtomizerDashboard("/atomizer");
Then browse to /atomizer to inspect jobs, job details, schedules, queue statistics, and active worker heartbeats. The dashboard also includes operational actions such as triggering registered job types, retrying failed jobs, cancelling pending jobs, enabling or disabling schedules, and running schedules immediately.
If no authorization filters are configured, dashboard requests are restricted to localhost by default. Add an IAtomizerDashboardAuthorizationFilter through AddAtomizerDashboard(options => options.Authorization.Add(...)) before exposing it outside local development.
For production, prefer integrating with ASP.NET Core authorization:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AtomizerDashboard", policy =>
policy.RequireAuthenticatedUser().RequireRole("Operations"));
});
builder.Services.AddAtomizerDashboard(options =>
{
options.Authorization.RequirePolicy("AtomizerDashboard");
});
Make sure the host app runs its authentication middleware before mapping the dashboard:
app.UseAuthentication();
app.UseAuthorization();
app.MapAtomizerDashboard("/atomizer");
The dashboard also provides shortcuts for common requirements:
builder.Services.AddAtomizerDashboard(options =>
{
options.Authorization.RequireAuthenticatedUser();
// or: options.Authorization.RequireRoles("Operations", "Admin");
// or: options.Authorization.RequireClaim("scope", "atomizer.dashboard.read");
});
Multiple dashboard authorization filters are evaluated as alternatives; the first filter that authorizes a request allows it.
Basic authentication is available as an explicit opt-in filter. It requires HTTPS by default:
builder.Services.AddAtomizerDashboard(options =>
{
options.Authorization.RequireBasicAuthentication(
builder.Configuration["AtomizerDashboard:Username"]!,
builder.Configuration["AtomizerDashboard:Password"]!);
});
For credentials stored outside configuration, use the async validator overload:
builder.Services.AddAtomizerDashboard(options =>
{
options.Authorization.RequireBasicAuthentication(async (context, username, password) =>
{
var validator = context.RequestServices.GetRequiredService<IDashboardCredentialValidator>();
return await validator.ValidateAsync(username, password, context.RequestAborted);
});
});
Custom authorization filters can also do asynchronous work:
public sealed class DashboardAuthorizationFilter : IAtomizerDashboardAuthorizationFilter
{
public async ValueTask<DashboardAuthorizationResult> AuthorizeAsync(HttpContext context)
{
var access = context.RequestServices.GetRequiredService<IDashboardAccessService>();
return await access.CanReadDashboardAsync(context.User, context.RequestAborted)
? DashboardAuthorizationResult.Authorized
: DashboardAuthorizationResult.Forbidden;
}
}
Dashboard API calls are same-origin requests and include same-origin credentials, so cookie, Windows, and other host-level authentication schemes can flow naturally. If your setup needs the embedded frontend to attach an extra request header, configure it when serving the dashboard HTML:
builder.Services.AddAtomizerDashboard(options =>
{
options.Authorization.RequirePolicy("AtomizerDashboard");
options.Client.RequestHeaders.Add("X-CSRF-TOKEN", context => context.Request.Cookies["X-CSRF-TOKEN"]);
});
Do not put long-lived shared secrets in Client.RequestHeaders; those values are rendered into the dashboard page and are visible to the browser. Use them for request metadata such as CSRF tokens or short-lived per-user tokens.
Contributing
- Fork the repository.
- Create a new branch (feature/xyz).
- Commit your changes with clear messages.
- Submit a PR with details of your changes and test coverage.
License
This project is licensed under the MIT License. See the LICENSE file for details
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- Atomizer (>= 0.6.2)
- Microsoft.EntityFrameworkCore (>= 6.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 6.0.0)
-
net6.0
- Atomizer (>= 0.6.2)
- Microsoft.EntityFrameworkCore (>= 6.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 6.0.0)
-
net8.0
- Atomizer (>= 0.6.2)
- Microsoft.EntityFrameworkCore (>= 6.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 6.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.6.3 | 83 | 5/16/2026 |
| 0.6.2 | 88 | 5/16/2026 |
| 0.6.1 | 84 | 5/16/2026 |
| 0.6.0 | 85 | 5/16/2026 |
| 0.5.0 | 85 | 5/14/2026 |
| 0.5.0-rc.3 | 53 | 5/14/2026 |
| 0.5.0-rc.2 | 46 | 5/13/2026 |
| 0.5.0-rc.1 | 44 | 5/13/2026 |
| 0.4.0 | 94 | 5/5/2026 |
| 0.3.0 | 89 | 5/4/2026 |
| 0.2.0 | 122 | 3/10/2026 |
| 0.1.3 | 263 | 8/29/2025 |
| 0.1.2 | 283 | 8/26/2025 |
| 0.1.1 | 167 | 8/22/2025 |
| 0.1.0 | 201 | 8/21/2025 |
| 0.0.1 | 210 | 8/20/2025 |