ZeroAlloc.Outbox.EfCore 4.4.5

dotnet add package ZeroAlloc.Outbox.EfCore --version 4.4.5
                    
NuGet\Install-Package ZeroAlloc.Outbox.EfCore -Version 4.4.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ZeroAlloc.Outbox.EfCore" Version="4.4.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ZeroAlloc.Outbox.EfCore" Version="4.4.5" />
                    
Directory.Packages.props
<PackageReference Include="ZeroAlloc.Outbox.EfCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ZeroAlloc.Outbox.EfCore --version 4.4.5
                    
#r "nuget: ZeroAlloc.Outbox.EfCore, 4.4.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ZeroAlloc.Outbox.EfCore@4.4.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ZeroAlloc.Outbox.EfCore&version=4.4.5
                    
Install as a Cake Addin
#tool nuget:?package=ZeroAlloc.Outbox.EfCore&version=4.4.5
                    
Install as a Cake Tool

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

Product Compatible and additional computed target framework versions.
.NET 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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
4.4.5 0 10/16/2026
4.4.4 177 10/15/2026
4.4.3 397 10/12/2026
4.4.2 232 10/11/2026
4.4.1 243 10/11/2026
4.4.0 289 10/10/2026
4.3.0 166 10/10/2026
4.2.0 361 9/30/2026
4.1.0 256 9/30/2026
4.0.1 220 9/28/2026
4.0.0 135 9/27/2026
3.0.1 714 9/26/2026
3.0.0 114 9/26/2026
2.7.2 189 9/25/2026
2.7.1 314 9/22/2026
2.7.0 87 9/22/2026
2.6.0 118 9/20/2026
2.5.5 113 9/20/2026
2.5.4 344 9/20/2026
2.5.3 101 9/19/2026
Loading failed