OrionRelay.EntityFrameworkCore
0.5.0
dotnet add package OrionRelay.EntityFrameworkCore --version 0.5.0
NuGet\Install-Package OrionRelay.EntityFrameworkCore -Version 0.5.0
<PackageReference Include="OrionRelay.EntityFrameworkCore" Version="0.5.0" />
<PackageVersion Include="OrionRelay.EntityFrameworkCore" Version="0.5.0" />
<PackageReference Include="OrionRelay.EntityFrameworkCore" />
paket add OrionRelay.EntityFrameworkCore --version 0.5.0
#r "nuget: OrionRelay.EntityFrameworkCore, 0.5.0"
#:package OrionRelay.EntityFrameworkCore@0.5.0
#addin nuget:?package=OrionRelay.EntityFrameworkCore&version=0.5.0
#tool nuget:?package=OrionRelay.EntityFrameworkCore&version=0.5.0
OrionRelay.EntityFrameworkCore
A durable Entity Framework Core dead-letter sink for
OrionRelay. It implements the existing
IDeadLetterSink over a relational table, so webhook deliveries that exhaust their attempt budget
survive a process restart and are shared across instances, instead of being held in the
process-local in-memory sink that loses its entries on restart.
Part of the Orion family.
What it does
- Implements
IDeadLetterSinkover EF Core. The interface is not widened: this is a durable reference implementation behind the existing seam, the half of the dead-letter story the in-memory sink left open. - Persists the whole abandoned delivery. Each
DeadLetterEntrythe dispatcher routes here is stored as aDeadLetterRecord: the target endpoint, the payload, the content type and event headers a receiver would have seen, the attempt count, the final error, the last HTTP status, and the abandonment timestamp. - Idempotent on the delivery id.
DeliveryIdis the primary key, so when the dispatcher re-routes a replayed terminal delivery the second write resolves to the existing row rather than inserting a duplicate. It is the message'sEventIdwhen one was set; otherwise a surrogate keys the row, since an unidentified delivery has nothing stable to deduplicate on. - Exposes a read-back path for triage.
GetHeldAsyncreturns the parked deliveries newest first (with an optional cap) andCountAsynccounts them, for inspection and a later replay. These are additive queries on the concrete store, not on theIDeadLetterSinkinterface. - Provider agnostic: depends only on
Microsoft.EntityFrameworkCore.Relational, so you choose the database provider (SQL Server, PostgreSQL, SQLite, and so on).
Install
dotnet add package OrionRelay.EntityFrameworkCore
You also need an EF Core provider package for your database, for example
Microsoft.EntityFrameworkCore.SqlServer or Npgsql.EntityFrameworkCore.PostgreSQL.
Quick start
Register the sink before AddOrionRelay(), configuring the context inline. The bundled
OrionRelayDeadLetterDbContext is ready to use:
using Moongazing.OrionRelay.EntityFrameworkCore;
// Register the durable sink first: AddOrionRelay only adds the no-op sink if no IDeadLetterSink
// is already present. This also registers a context factory the sink resolves a short-lived
// context from per abandoned delivery.
builder.Services.AddOrionRelayEntityFrameworkCoreDeadLetterSink(o =>
o.UseSqlServer(builder.Configuration.GetConnectionString("Webhooks")));
builder.Services.AddOrionRelay(signingSecret: "whsec_your_shared_secret");
A delivery that exhausts its attempt budget is now parked in the database instead of discarded.
Inspecting held deliveries
IDeadLetterSink is write-only by design. To triage what is parked, resolve the concrete store and
query it:
var sink = app.Services.GetRequiredService<EntityFrameworkCoreDeadLetterSink<OrionRelayDeadLetterDbContext>>();
var held = await sink.GetHeldAsync(limit: 100, ct);
foreach (var record in held)
{
Console.WriteLine($"{record.DeliveryId} -> {record.Endpoint} after {record.Attempts} attempts");
}
Using your own DbContext
If you already have a context, host the dead-letter table in it by applying the configuration in
OnModelCreating, then point the sink at that context:
public sealed class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new DeadLetterRecordConfiguration());
// ... your own entities
}
}
builder.Services.AddOrionRelayEntityFrameworkCoreDeadLetterSink<AppDbContext>(o =>
o.UseSqlServer(connectionString));
DeadLetterRecordConfiguration also accepts a custom table name if the default
OrionRelayDeadLetters clashes with an existing table.
Migrations
The sink does not create the schema. Add a migration for the mapped entity the usual way and apply it as part of your deployment:
dotnet ef migrations add AddOrionRelayDeadLetters
dotnet ef database update
Versioning
Multi-targets net8.0, net9.0, and net10.0, pinning the matching EF Core major per target
framework. Tracks the OrionRelay version line. See the
root README and
CHANGELOG.
License
Licensed under the MIT License.
| 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
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.0 && < 11.0.0)
- OrionRelay (>= 0.5.0)
-
net8.0
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.10 && < 9.0.0)
- OrionRelay (>= 0.5.0)
-
net9.0
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.0 && < 10.0.0)
- OrionRelay (>= 0.5.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.