Crucible.Common.EntityEvents 0.2.0

dotnet add package Crucible.Common.EntityEvents --version 0.2.0
                    
NuGet\Install-Package Crucible.Common.EntityEvents -Version 0.2.0
                    
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="Crucible.Common.EntityEvents" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Crucible.Common.EntityEvents" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Crucible.Common.EntityEvents" />
                    
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 Crucible.Common.EntityEvents --version 0.2.0
                    
#r "nuget: Crucible.Common.EntityEvents, 0.2.0"
                    
#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 Crucible.Common.EntityEvents@0.2.0
                    
#: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=Crucible.Common.EntityEvents&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Crucible.Common.EntityEvents&version=0.2.0
                    
Install as a Cake Tool

Crucible.Common.EntityEvents

Entity event infrastructure for Entity Framework Core. Automatically generates events when entities are created, updated, or deleted.

Features

  • Automatic event generation - Events are created from EF Core change tracking
  • Transaction-aware - Events are generated after transaction commit to ensure consistency
  • Framework-agnostic - No dependency on specific event publishing libraries (MediatR, etc.)
  • Source generator included - Automatically add interfaces like INotification via attribute
  • Simple registration - One method call configures everything

Installation

dotnet add package Crucible.Common.EntityEvents

Quick Start

1. Create Your DbContext

Inherit from EventPublishingDbContext and implement the PublishEventsAsync method:

using Crucible.Common.EntityEvents.Abstractions;
using Microsoft.Extensions.Logging;

[GenerateEntityEventInterfaces(typeof(INotification))]
public class MyContext : EventPublishingDbContext
{
    public DbSet<MyEntity> MyEntities { get; set; }

    public MyContext(DbContextOptions<MyContext> options) : base(options) { }

    public override async Task PublishEventsAsync(IReadOnlyList<IEntityEvent> events, CancellationToken cancellationToken)
    {
        if (ServiceProvider is not null)
        {
            var mediator = ServiceProvider.GetRequiredService<IMediator>();
            var logger = ServiceProvider.GetRequiredService<ILogger<MyContext>>();

            foreach (var evt in events.Cast<INotification>())
            {
                try
                {
                    await mediator.Publish(evt, cancellationToken);
                }
                catch (Exception ex)
                {
                    logger.LogError(ex, "Error publishing entity event {EventType}", evt.GetType().Name);
                }
            }
        }
    }
}

The EntityEventInterceptor handles calling PublishEventsAsync at the right time (after SaveChanges for non-transactional operations, or after transaction commit). Events are passed as a parameter — the interceptor creates the events and clears its tracked state before calling your method. You can use the publishing mechanism of your choice. The example above uses MediatR.

2. Register Services

Use the AddEventPublishingDbContextFactory extension method for simple registration:

using Crucible.Common.EntityEvents.Extensions;

services.AddEventPublishingDbContextFactory<MyContext>((sp, builder) =>
    builder.UseNpgsql(connectionString));

This single call:

  • Registers the EntityEventInterceptor
  • Creates a pooled DbContext factory with the interceptor configured
  • Registers a scoped MyContext with ServiceProvider injected
Alternative: Manual Registration

If you need more control over registration:

services.AddEntityEventInterceptor();
services.AddPooledDbContextFactory<MyContext>((sp, builder) => builder
    .AddInterceptors(sp.GetRequiredService<EntityEventInterceptor>())
    .UseNpgsql(connectionString));

// Register scoped context with ServiceProvider injection
services.AddScoped(sp =>
{
    var factory = sp.GetRequiredService<IDbContextFactory<MyContext>>();
    var context = factory.CreateDbContext();
    context.ServiceProvider = sp;
    return context;
});

Publishing Integration

Publishing libraries may require specific interfaces be present on their published classes. To support this without tying this library to any specific publishing mechanism, all IEntityEvent classes are partial, allowing for them to be extended in the consuming application with any required interfaces.

Add the GenerateEntityEventInterfaces attribute directly on your DbContext class:

using Crucible.Common.EntityEvents;
using MediatR;

[GenerateEntityEventInterfaces(typeof(INotification))]
public class MyContext : EventPublishingDbContext
{
    // ...
}

The source generator automatically creates partial class declarations that implement the passed in types for all entity event types. You can pass multiple types:

[GenerateEntityEventInterfaces(typeof(INotification), typeof(IMyCustomInterface))]

Manual (Legacy)

If the source generator doesn't work in your environment, create partial class extensions manually:

using Crucible.Common.EntityEvents.Events;
using MediatR;

namespace Crucible.Common.EntityEvents.Events;

public partial class EntityCreated<TEntity> : INotification { }
public partial class EntityUpdated<TEntity> : INotification { }
public partial class EntityDeleted<TEntity> : INotification { }

Event Types

  • EntityCreated<TEntity> - Raised when an entity is added
  • EntityUpdated<TEntity> - Raised when an entity is modified (includes ModifiedProperties)
  • EntityDeleted<TEntity> - Raised when an entity is deleted

How It Works

  1. EntityEventInterceptor intercepts SavingChanges to capture entity states
  2. After transaction commits (or SaveChanges completes if no transaction), events are created
  3. The interceptor calls your PublishEventsAsync implementation to publish the events
  4. The interceptor automatically clears events and tracked state after publishing
  5. If a transaction is rolled back, tracked state is cleared without publishing

This ensures events are only published for changes that actually persisted to the database.

Advanced: Manual IEventPublishingDbContext Implementation

If you need full control, implement IEventPublishingDbContext directly:

using Crucible.Common.EntityEvents.Abstractions;

public class MyContext : DbContext, IEventPublishingDbContext
{
    public IServiceProvider? ServiceProvider { get; set; }
    public List<TrackedEntityEntry> TrackedEntries { get; } = [];

    public Task PublishEventsAsync(IReadOnlyList<IEntityEvent> events, CancellationToken ct = default)
    {
        // Your event publishing logic here.
        // The interceptor creates events and passes them to this method.
        return Task.CompletedTask;
    }
}

Note: TrackedEntries is used internally by the interceptor to track entity changes across multiple SaveChanges calls within a transaction. It must be stored on the DbContext (not the interceptor) for thread safety when using pooled contexts.

Product Compatible and additional computed target framework versions.
.NET 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.
  • net10.0

    • No dependencies.

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.2.0 1,864 3/16/2026
0.1.1 140 3/10/2026
0.1.0 564 2/11/2026