OrionPatch.EntityFrameworkCore
0.3.2
See the version list below for details.
dotnet add package OrionPatch.EntityFrameworkCore --version 0.3.2
NuGet\Install-Package OrionPatch.EntityFrameworkCore -Version 0.3.2
<PackageReference Include="OrionPatch.EntityFrameworkCore" Version="0.3.2" />
<PackageVersion Include="OrionPatch.EntityFrameworkCore" Version="0.3.2" />
<PackageReference Include="OrionPatch.EntityFrameworkCore" />
paket add OrionPatch.EntityFrameworkCore --version 0.3.2
#r "nuget: OrionPatch.EntityFrameworkCore, 0.3.2"
#:package OrionPatch.EntityFrameworkCore@0.3.2
#addin nuget:?package=OrionPatch.EntityFrameworkCore&version=0.3.2
#tool nuget:?package=OrionPatch.EntityFrameworkCore&version=0.3.2
OrionPatch.EntityFrameworkCore
EF Core storage backend for OrionPatch. Adds the OrionPatch_Outbox table with provider-aware competing-consumers claim and a SaveChangesInterceptor that flushes buffered messages into your transaction.
30-second quick start
services.AddDbContext<AppDbContext>((sp, options) =>
{
options.UseNpgsql(connectionString);
options.UseOrionPatch(sp); // hook the interceptor into your DbContext
});
services.AddOrionPatch()
.UseEntityFrameworkCore<AppDbContext>()
.UseSink<MyKafkaSink>();
In your DbContext.OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder) =>
modelBuilder.ApplyOrionPatchConfiguration();
Enqueue from service code:
_outbox.Enqueue(new OrderConfirmed(orderId, totalCents));
await _db.SaveChangesAsync(ct); // outbox row commits with your other entity changes
What's in the box
OutboxEntityConfiguration— mapsOutboxRowtoOrionPatch_Outboxwith covering indexes for the dispatcher's polling and lease-expiry queries.EfCoreOutbox—IOutboxthat buffers per-DbContext and binds viaConditionalWeakTableso the interceptor finds the right buffer without a service-provider hop.OrionPatchSaveChangesInterceptor— six-override lifecycle (Saving / Saved / SaveChangesFailed and their async siblings) implementing a three-phase Flush / Commit / Revert so save failures re-buffer cleanly without double-inserting on retry.EfCoreOutboxStorage— claim/complete/fail/dead-letter viaExecuteUpdateAsyncsingle round-trips. From v0.3.2 it also implementsIDeadLetterStoreandIOutboxArchivalStore(see below).- Provider-aware claim strategy:
- SqlServer / PostgreSQL / MySQL —
SkipLockedClaimStrategywith dialect-specific SQL. (v0.1.0 currently delegates these to the portable fallback; trueSKIP LOCKEDSQL lands in v0.2.) - SQLite + unknown providers —
CompareAndSwapClaimStrategy(portable optimistic-concurrency claim).
- SqlServer / PostgreSQL / MySQL —
Dead-letter and archival (v0.3.2)
EfCoreOutboxStorage implements the v0.3.0 IDeadLetterStore and IOutboxArchivalStore SPIs against two new tables, so deployments get the durable dead-letter destination and the retention reaper without writing their own storage.
OrionPatch_DeadLetter(DeadLetterRow) — the dispatcher routes a row that exhaustsMaxAttemptshere instead of flipping it toDeadLetteredin place. The move (delete the source outbox row, insert the snapshot) runs in one transaction so it is atomic, and the dead-letter primary key is the source row id so a crash-replayed terminal path lands the message exactly once. Read it back withGetDeadLetteredAsync()(newest first, capped) or the pagedGetDeadLetteredAsync(skip, take, ct)overload for triage over a large backlog.OrionPatch_OutboxArchive(OutboxArchiveRow) —ArchiveProcessedAsync(retention, nowUtc, ct)reapsProcessedrows older than the retention cutoff out of the hot outbox in bounded batches and returns the count moved. Archive mode (default) copies them here first; purge mode skips the table and deletes outright. This is operator-invoked — OrionPatch does not start a background reaper, so call it from your own scheduled job.
Choose the mode at registration:
services.AddOrionPatch()
.UseEntityFrameworkCore<AppDbContext>(); // archive mode (default)
// or
services.AddOrionPatch()
.UseEntityFrameworkCore<AppDbContext>(purgeOnArchive: true); // purge mode
Invoke the reaper from a scheduled job, resolving the archival store from a scope:
using var scope = serviceProvider.CreateScope();
var archival = scope.ServiceProvider.GetRequiredService<IOutboxArchivalStore>();
var reaped = await archival.ArchiveProcessedAsync(options.ArchiveRetention, DateTime.UtcNow, ct);
Migration
The runtime never creates tables. After upgrading to v0.3.2, ApplyOrionPatchConfiguration() adds the two new tables to your model; regenerate and apply a migration so they exist before the dead-letter store or the reaper runs:
dotnet ef migrations add OrionPatch_v0_3_2_DeadLetterAndArchive
dotnet ef database update
The two new tables are inert until you route a dead-letter or call ArchiveProcessedAsync, so applying the migration is safe to do ahead of turning either path on. No existing column or index changes — this migration is purely additive.
Multi-DbContext
v0.1.0 supports one OrionPatch-bound DbContext per host. Calling UseEntityFrameworkCore<TDbContext> twice silently overrides the previous registration's IOutbox and IOutboxStorage services. First-class multi-DbContext support is on the v0.2 roadmap.
See the repo README for the full picture.
| 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 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. |
-
net10.0
- Confluent.Kafka (>= 2.6.0)
- Microsoft.EntityFrameworkCore (>= 8.0.10)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.10)
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions (>= 8.0.10)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.1)
- Microsoft.Extensions.Logging (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options (>= 8.0.2)
- OrionPatch (>= 0.3.2)
- OrionPatch.Kafka (>= 0.3.2)
-
net8.0
- Confluent.Kafka (>= 2.6.0)
- Microsoft.EntityFrameworkCore (>= 8.0.10)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.10)
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions (>= 8.0.10)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.1)
- Microsoft.Extensions.Logging (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options (>= 8.0.2)
- OrionPatch (>= 0.3.2)
- OrionPatch.Kafka (>= 0.3.2)
-
net9.0
- Confluent.Kafka (>= 2.6.0)
- Microsoft.EntityFrameworkCore (>= 8.0.10)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.10)
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions (>= 8.0.10)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.1)
- Microsoft.Extensions.Logging (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options (>= 8.0.2)
- OrionPatch (>= 0.3.2)
- OrionPatch.Kafka (>= 0.3.2)
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.4.2 | 114 | 7/27/2026 |
| 0.4.1 | 111 | 6/30/2026 |
| 0.4.0 | 110 | 6/28/2026 |
| 0.3.3 | 114 | 6/27/2026 |
| 0.3.2 | 110 | 6/22/2026 |
| 0.3.1 | 151 | 6/20/2026 |
| 0.3.0 | 124 | 6/19/2026 |
| 0.2.32 | 125 | 6/17/2026 |
| 0.2.31 | 133 | 6/16/2026 |
| 0.2.30 | 115 | 6/16/2026 |
| 0.2.29 | 107 | 6/15/2026 |
| 0.2.25 | 109 | 6/12/2026 |
| 0.2.24 | 115 | 6/12/2026 |
| 0.2.23 | 113 | 6/11/2026 |
| 0.2.22 | 129 | 6/11/2026 |
| 0.2.21 | 108 | 6/11/2026 |
| 0.2.20 | 109 | 6/11/2026 |
| 0.2.19 | 114 | 6/11/2026 |
| 0.2.18 | 108 | 6/11/2026 |
| 0.2.17 | 113 | 6/11/2026 |