Swevo.EFCore.Outbox
2.0.0
Prefix Reserved
dotnet add package Swevo.EFCore.Outbox --version 2.0.0
NuGet\Install-Package Swevo.EFCore.Outbox -Version 2.0.0
<PackageReference Include="Swevo.EFCore.Outbox" Version="2.0.0" />
<PackageVersion Include="Swevo.EFCore.Outbox" Version="2.0.0" />
<PackageReference Include="Swevo.EFCore.Outbox" />
paket add Swevo.EFCore.Outbox --version 2.0.0
#r "nuget: Swevo.EFCore.Outbox, 2.0.0"
#:package Swevo.EFCore.Outbox@2.0.0
#addin nuget:?package=Swevo.EFCore.Outbox&version=2.0.0
#tool nuget:?package=Swevo.EFCore.Outbox&version=2.0.0
Swevo.EFCore.Outbox
[](https://www.nuget.org/packages/Swevo.EFCore.Outbox/)
Transactional outbox pattern for EF Core + AutoBus. Enqueue domain events inside your existing SaveChanges transaction and publish them reliably via a background processor — zero message loss even if the bus is temporarily unavailable.
v2.0.0 breaking change: MassTransit's
IPublishEndpointhas been replaced with Swevo.AutoBus'sIMessageBus— a free, MIT-licensed alternative now that MassTransit v9 is commercial-only. See Migrating from MassTransit below.
How It Works
┌─────────────────────────────────────────────────┐
│ Your service │
│ │
│ 1. outbox.Add(new OrderPlaced(...)) │
│ 2. dbContext.SaveChangesAsync() │
│ │
│ ┌──────────────────┐ atomic ┌────────────┐ │
│ │ domain changes │──────────│ OutboxMsg │ │
│ └──────────────────┘ └────────────┘ │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ OutboxProcessor (BackgroundService) │
│ │
│ 3. SELECT * FROM OutboxMessages WHERE │
│ ProcessedAt IS NULL ORDER BY CreatedAt │
│ 4. bus.PublishAsync(message) │
│ 5. UPDATE ProcessedAt = NOW() │
└─────────────────────────────────────────────────┘
Installation
dotnet add package Swevo.EFCore.Outbox
Requires EF Core 8+ and Swevo.AutoBus 1.x. AutoBus is added automatically as a transitive dependency, but you must still call services.AddAutoBus(...) (with or without a transport, e.g. AddAutoBusRabbitMq) so IMessageBus is available in DI — see AutoBus's README for configuration.
Quick Start
1. Configure your DbContext
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
public DbSet<OutboxMessage> OutboxMessages => Set<OutboxMessage>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.AddOutboxMessages(); // registers the OutboxMessage entity
}
}
2. Register services
// Program.cs
builder.Services.AddOutbox<AppDbContext>(options =>
{
options.PollingInterval = TimeSpan.FromSeconds(5); // default: 10s
options.BatchSize = 50; // default: 100
});
builder.Services.AddDbContext<AppDbContext>((sp, options) =>
{
options.UseSqlServer(connectionString);
options.AddOutboxInterceptor(sp); // wires the scoped interceptor
});
3. Use in your services
public class OrderService(IOutbox outbox, AppDbContext db)
{
public async Task PlaceOrder(PlaceOrderCommand cmd)
{
db.Orders.Add(new Order { Id = cmd.OrderId, Total = cmd.Total });
// Enqueued atomically — written in the same SaveChanges transaction
outbox.Add(new OrderPlaced(cmd.OrderId, cmd.Total));
await db.SaveChangesAsync();
// ✓ Order row saved
// ✓ OutboxMessage row saved } in one DB transaction
// ✗ Bus not involved yet
}
}
4. Add EF migration
dotnet ef migrations add AddOutboxMessages
dotnet ef database update
Architecture
IOutbox (scoped)
Collects messages before SaveChanges. Injected into your service classes.
outbox.Add(new OrderPlaced(orderId, total)); // enqueue
outbox.Add(new PaymentCharged(paymentId, total)); // multiple per save cycle
OutboxInterceptor (scoped SaveChangesInterceptor)
Automatically runs during SaveChanges — no extra code needed after registration. Writes all pending IOutbox messages to the OutboxMessages table within the same database transaction.
OutboxProcessor<TContext> (BackgroundService)
Polls the OutboxMessages table, publishes via IMessageBus, and marks messages as processed. Handles errors per-message — a single failing publish doesn't block the rest of the batch.
OutboxOptions
| Property | Default | Description |
|---|---|---|
PollingInterval |
10 seconds | How often to check for pending messages |
BatchSize |
100 | Max messages processed per poll cycle |
Integration with AutoAudit
Use alongside AutoAudit to get both audit fields and reliable messaging:
[Auditable]
public partial class Order { ... }
// In your service:
db.Orders.Add(order);
outbox.Add(new OrderPlaced(order.Id));
await db.SaveChangesAsync();
// CreatedAt/UpdatedAt set by AuditInterceptor
// OrderPlaced written to OutboxMessages by OutboxInterceptor
// Both in one transaction
Compatibility
| Dependency | Version |
|---|---|
| EF Core | 8.0+ |
| Swevo.AutoBus | 1.x |
| .NET | net8.0+ |
Migrating from 1.x (MassTransit)
OutboxProcessor<TContext> now resolves AutoBus.IMessageBus from DI instead of MassTransit's IPublishEndpoint. The call site is a drop-in replacement — both expose Publish(object message, Type messageType, CancellationToken) with the same semantics — so the migration is:
- Remove your MassTransit bus registration (or keep it running side-by-side if you still need it for consumers elsewhere).
- Add Swevo.AutoBus:
dotnet add package Swevo.AutoBus(addSwevo.AutoBus.RabbitMQtoo if you need a real transport instead of in-memory). - Register it:
builder.Services.AddAutoBus(cfg => { /* register any AutoBus consumers */ }); - Upgrade
Swevo.EFCore.Outboxto2.0.0. No changes required toIOutbox,OutboxInterceptor, or your domain event types.
If you still need to publish onto an existing MassTransit-based system, keep MassTransit registered in your app and write a small IConsumer<T> in AutoBus that forwards to MassTransit's IPublishEndpoint — this keeps EFCore.Outbox's own dependency footprint free of MassTransit while still supporting a gradual migration.
Also by the same author
🌐 Full suite overview: swevo.github.io
| Package | Description |
|---|---|
| AutoLog.Generator | Compile-time high-performance logging — [Log(Level, Message)] generates LoggerMessage.Define. AOT-safe. |
| AutoHttpClient.Generator | Compile-time typed HTTP client — [HttpClient] on an interface generates a strongly-typed client. AOT-safe Refit alternative. |
| AutoDispatch.Generator | Compile-time CQRS dispatcher — [Handler] generates a strongly-typed IDispatcher. No MediatR, no reflection. |
| AutoWire | Compile-time DI auto-registration — [Scoped]/[Singleton]/[Transient] generates IServiceCollection registration code. |
| AutoMap.Generator | Compile-time object mapping with generated extension methods. AOT-safe AutoMapper alternative. |
License
MIT © 2025 Justin Bannister
| Product | Versions 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 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 was computed. 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. |
-
net8.0
- Microsoft.EntityFrameworkCore (>= 8.0.28)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.2)
- Swevo.AutoBus (>= 1.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.
2.0.0: BREAKING - Replaced MassTransit's IPublishEndpoint with Swevo.AutoBus's IMessageBus as the publish mechanism. Removes the MassTransit dependency entirely. Existing MassTransit users should register Swevo.AutoBus (with or without a transport) to get an IMessageBus in DI; see README for the migration guide. 1.0.1: Prior MassTransit-based release.