Domain-event collection and publishing during successful async saves for .NET developers using entity event queues or RaisesEvent declarations.
Use a .NET 10 console project. Install the package, then replace Program.cs with the example.
dotnet new console -f net10.0 -n EfCoreDemo
cd EfCoreDemo
dotnet add package DKNet.EfCore.Events
dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 10.0.12
using Microsoft.EntityFrameworkCore;
using DKNet.EfCore.Hooks;
using Microsoft.Extensions.DependencyInjection;
using var connection = new Microsoft.Data.Sqlite.SqliteConnection("Data Source=:memory:");
connection.Open();
var services = new Microsoft.Extensions.DependencyInjection.ServiceCollection();
services.AddLogging();
services.AddDbContextWithHook<DemoDb>(o => o.UseSqlite(connection));
services.AddEventPublisher<DemoDb, ConsolePublisher>();
using var provider = services.BuildServiceProvider();
using var scope = provider.CreateScope();
await using var db = scope.ServiceProvider.GetRequiredService<DemoDb>();
await db.Database.EnsureCreatedAsync();
var item = new Item();
item.Announce();
db.Add(item);
await db.SaveChangesAsync();
public sealed class DemoDb(DbContextOptions<DemoDb> options) : DbContext(options)
{
public DbSet<Item> Items => Set<Item>();
}
public sealed class Item : DKNet.EfCore.Abstractions.Entities.Entity<int>
{
public string Name { get; set; } = "Coffee";
public void Announce() => AddEvent(new ItemCreated(Name));
}
public sealed record ItemCreated(string Name);
public sealed class ConsolePublisher : DKNet.EfCore.Abstractions.Events.DefaultEventPublisher
{
public override Task PublishAsync(object value, CancellationToken cancellationToken = default)
{
Console.WriteLine(value); // ItemCreated { Name = Coffee }
return Task.CompletedTask;
}
}
Run with dotnet run.
EventHook runs as a scoped Hooks implementation and reads the shared Extensions snapshot. IEventPublisher implementations are scoped, while the first IMapper is used for mapping-based sources. The package supplies no durable transport, outbox store or background dispatcher.
AddEventPublisher registers the publisher and EventHook; AddDbContextWithHook installs the required interceptor. Publisher failures are logged and swallowed individually, while mapping errors propagate. An outer transaction may still be uncommitted when the after-save phase runs.
IEventPublisher implementations through the save hook.[RaisesEvent] rules before the write, then map qualifying payloads from the entity after a successful save.