Skip to main content
net10.0net8.0net9.0

Blazor component (<OutboxDashboard />) for ZeroAlloc.Outbox.Dashboard. Requires the Dashboard endpoints mapped via MapOutboxDashboard().

Published
Oct 16, 2026
3d ago
Total Downloads
1.8K
1,759

Install

dotnet add package ZeroAlloc.Outbox.Dashboard.Blazor --version 4.4.5
ProductVersions
.NET
net10.0net8.0net9.0

README

ZeroAlloc.Outbox

NuGet Build License: MIT AOT GitHub Sponsors

Source-generated transactional outbox for .NET: mark a message with [OutboxMessage] and get a typed writer, a polling worker with retry and dead-lettering, and NativeAOT support, within a few percent of a hand-written outbox.

Why ZeroAlloc.Outbox

  • Within 2–9% of a hand-rolled outbox. Against a correctness-matched SQLite outbox on the same connection, an enqueue is +2% time and a 10-message dispatch tick +9%, with 7% less allocated.
  • Allocates only the payload. An enqueue allocates just the exact-size payload array, 72 B for a small struct message. The worker loop adds nothing per message beyond deserialising it, and a MemoryPack struct deserialises with 0 B. All three are asserted by tests.
  • Works under NativeAOT and trimming. Writers and dispatchers are generated at compile time, with no Type.GetType or MakeGenericType. CI publishes and runs an AOT app with IL warnings as errors.
  • Compile-time diagnostics. ZO0001 to ZO0007 flag message types the generator cannot handle, such as interfaces, nested types or colliding names, at build time.

Install

dotnet add package ZeroAlloc.Outbox
dotnet add package ZeroAlloc.Outbox.InMemory

The source generator ships inside ZeroAlloc.Outbox. The quick start uses the in-memory store; production apps use the ORM or EF Core store below.

Quick start

using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ZeroAlloc.Outbox;
using ZeroAlloc.Outbox.InMemory;
using ZeroAlloc.Serialisation;

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSerializerDispatcher(); // generated, AOT-safe
builder.Services.AddOutbox(o => o.PollingInterval = TimeSpan.FromMilliseconds(100))
    .WithInMemoryStore()       // or .WithOrm() / .WithEfCore<AppDbContext>() in production
    .AddOrderPlacedOutbox();   // generated for the [OutboxMessage] type
builder.Services.AddSingleton<IOutboxDispatcher<OrderPlaced>, SendOrderPlaced>();
using var host = builder.Build();
await host.StartAsync();

await using (var scope = host.Services.CreateAsyncScope())
{
    var outbox = scope.ServiceProvider.GetRequiredService<IOutboxWriter<OrderPlaced>>();
    await outbox.WriteAsync(new OrderPlaced(42, 9.95m)); // pass your DbTransaction for an atomic write
}
await Task.Delay(500); // the worker claims the message and dispatches it
await host.StopAsync();

[OutboxMessage, ZeroAllocSerializable(SerializationFormat.SystemTextJson)]
public sealed record OrderPlaced(int OrderId, decimal Amount);

[JsonSerializable(typeof(OrderPlaced))]
internal sealed partial class AppJson : JsonSerializerContext;

public sealed class SendOrderPlaced : IOutboxDispatcher<OrderPlaced>
{
    public ValueTask DispatchAsync(OrderPlaced message, CancellationToken ct)
    {
        Console.WriteLine($"Dispatched order {message.OrderId}"); // publish to your bus here
        return ValueTask.CompletedTask;
    }
}

ZeroAllocSerializable and the generated AddSerializerDispatcher() come from ZeroAlloc.Serialisation, which ZeroAlloc.Outbox brings in. If you don't need AOT, .WithSystemTextJsonSerializer() is a one-line reflection-based alternative. The getting started guide walks through the EF Core setup.

What you get

  • A typed IOutboxWriter<T> per message type that writes on your own database transaction.
  • Typed dispatchers: implement IOutboxDispatcher<T> once and send to any bus, HTTP endpoint or mailbox.
  • A background worker with lease-based claiming, so several hosts never dispatch the same row twice.
  • Exponential-backoff retry and dead-lettering with the last failure reason.
  • Named pipelines, each with its own store, options and worker.

Stores and integrations:

Outbox dashboard, pending tab

Performance

Against a hand-rolled SQLite outbox on the same connection, both transactional. .NET 10.0.7, i9-12900HK, BenchmarkDotNet v0.15.4.

Operation Hand-rolled ZA.Outbox Overhead
Enqueue (1 message, transactional) 6.86 µs / 2.08 KB 6.99 µs / 2.13 KB +2% time, +2% alloc
Dispatch tick (10 messages) 105.4 µs / 11.9 KB 115.0 µs / 11.09 KB +9% time, −7% alloc

Full numbers, including enqueue serialisation and dispatch deserialisation budgets: docs/performance.md.

Learn more

Changes from Previous Version

No structural changes between v4.4.4 and v4.4.5.

Adoption Assessment

Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.

Compatibility metadata lists 3 target frameworks; confirm the exact target used by your application.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.