CSharpEssentials.EntityFrameworkCore
3.0.2
See the version list below for details.
dotnet add package CSharpEssentials.EntityFrameworkCore --version 3.0.2
NuGet\Install-Package CSharpEssentials.EntityFrameworkCore -Version 3.0.2
<PackageReference Include="CSharpEssentials.EntityFrameworkCore" Version="3.0.2" />
<PackageVersion Include="CSharpEssentials.EntityFrameworkCore" Version="3.0.2" />
<PackageReference Include="CSharpEssentials.EntityFrameworkCore" />
paket add CSharpEssentials.EntityFrameworkCore --version 3.0.2
#r "nuget: CSharpEssentials.EntityFrameworkCore, 3.0.2"
#:package CSharpEssentials.EntityFrameworkCore@3.0.2
#addin nuget:?package=CSharpEssentials.EntityFrameworkCore&version=3.0.2
#tool nuget:?package=CSharpEssentials.EntityFrameworkCore&version=3.0.2
CSharpEssentials.EntityFrameworkCore
CSharpEssentials.EntityFrameworkCore offers a set of extensions, interceptors, and base classes to enhance Entity Framework Core development. It includes robust pagination support, audit trails, domain event dispatching, slow query logging, and simplified DbContext configuration.
Features
- Audit Interceptor: Automatically populates
CreatedAt/CreatedBy/UpdatedAt/UpdatedByfields and handles soft deletes. - Domain Event Interceptor: Collects and dispatches domain events raised by entities, with before/after save timing and optional outbox pattern support.
- Slow Query Logging:
SlowQueryInterceptorto automatically log queries exceeding a configurable threshold. - Advanced Pagination: Extension methods for Offset-based and Cursor-based pagination on
IQueryable. - BaseDbContext: A base class (
BaseDbContext<T>) with built-in logging and scope management. - Model Configuration: Extensions to apply configurations easily.
Installation
dotnet add package CSharpEssentials.EntityFrameworkCore
Usage
1. Audit Interceptor
Automatically tracks who created, modified, or deleted entities and when.
using CSharpEssentials.EntityFrameworkCore.Interceptors;
// Simple registration — no need to create a separate class
services.AddAuditInterceptor(() => "system-user");
// Or resolve user ID from DI (e.g. from HttpContext)
services.AddAuditInterceptor(sp =>
sp.GetRequiredService<IHttpContextAccessor>()
.HttpContext?.User?.FindFirst("sub")?.Value ?? "anonymous");
// Generic variant for typed user IDs
services.AddAuditInterceptor<Guid>(sp =>
sp.GetRequiredService<ICurrentUserService>().UserId);
// Register with DbContext
services.AddDbContext<MyDbContext>((sp, options) =>
options.UseSqlServer(connectionString)
.AddInterceptors(sp.GetRequiredService<AuditInterceptor>()));
The interceptor handles:
- Added entities (
ICreationAudit) — setsCreatedAtandCreatedBy - Modified entities (
IModificationAudit) — setsUpdatedAtandUpdatedBy - Deleted entities (
ISoftDeletable) — converts to soft delete withDeletedAt,DeletedBy, andIsDeleted
2. Domain Event Interceptor
Collects domain events raised by entities and dispatches them during SaveChanges.
using CSharpEssentials.EntityFrameworkCore.Interceptors;
// Register publisher and interceptor
services.AddSingleton<IDomainEventPublisher, MediatRDomainEventPublisher>();
services.AddSingleton<DomainEventInterceptor>();
services.AddDbContext<MyDbContext>((sp, options) =>
options.UseSqlServer(connectionString)
.AddInterceptors(sp.GetRequiredService<DomainEventInterceptor>()));
Event Timing
Control when events are published relative to the database save:
using CSharpEssentials.Entity;
using CSharpEssentials.Entity.Interfaces;
// AfterSave (default) — published after commit, safe for notifications
public sealed class OrderCreatedEvent(Guid orderId) : IDomainEvent
{
public Guid OrderId { get; } = orderId;
}
// BeforeSave — published before commit, can abort the save on failure
[DomainEventTiming(DomainEventTiming.BeforeSave)]
public sealed class OrderValidationEvent(Guid orderId) : IDomainEvent
{
public Guid OrderId { get; } = orderId;
}
Raising Events
// In your entity (inherits from EntityBase which implements IDomainEventHolder)
public void UpdatePrice(decimal newPrice)
{
decimal oldPrice = Price;
Price = newPrice;
Raise(new PriceChangedEvent(Id, oldPrice, newPrice));
}
Events are collected entity-by-entity in ChangeTracker order, preserving the list index within each entity.
Outbox Pattern
Register an IDomainEventOutbox for reliable delivery. AfterSave events are routed to the outbox; BeforeSave events are always published directly.
services.AddSingleton<IDomainEventOutbox, EfCoreOutbox>();
3. Slow Query Logging
Register the interceptor to find performance bottlenecks.
using CSharpEssentials.EntityFrameworkCore.Interceptors;
// Default threshold: 1 second
services.AddSlowQueryInterceptor();
// Custom threshold
services.AddSlowQueryInterceptor(TimeSpan.FromMilliseconds(500));
// With configure action
services.AddSlowQueryInterceptor(options =>
options.Threshold = TimeSpan.FromSeconds(2));
// Register with DbContext
services.AddDbContext<MyDbContext>((sp, options) =>
options.UseSqlServer(connectionString)
.AddInterceptors(sp.GetRequiredService<SlowQueryInterceptor>()));
Queries taking longer than the threshold will be logged as Warnings.
Custom Slow Query Handler
Implement ISlowQueryHandler for custom metrics or alerting:
public sealed class PrometheusSlowQueryHandler : ISlowQueryHandler
{
public void OnSlowQuery(SlowQueryContext context)
{
// Push to Prometheus, OpenTelemetry, etc.
SlowQueryCounter.Inc();
}
}
services.AddSingleton<ISlowQueryHandler, PrometheusSlowQueryHandler>();
4. Pagination (Offset-based)
Easily paginate any IQueryable.
using CSharpEssentials.EntityFrameworkCore.Pagination;
using CSharpEssentials.EntityFrameworkCore.Pagination.Requests;
var request = new PaginationRequest { PageNumber = 1, PageSize = 10 };
var response = await dbContext.Users
.PaginateAsync(request, cancellationToken: ct);
var items = response.Data;
var total = response.TotalCount;
5. Cursor Pagination (High Performance)
Use cursor-based pagination for infinite scroll scenarios or large datasets where offset pagination is slow.
using CSharpEssentials.EntityFrameworkCore.Pagination;
var request = new CursorPaginationRequest<DateTimeOffset>
{
Limit = 20,
Cursor = lastSeenDate
};
var response = await dbContext.Logs
.PaginateAsync(
request,
cursorSelector: x => x.CreatedAt,
isAscending: false
);
var nextCursor = response.NextCursor;
Supports any IComparable<T> cursor type: string, int, Guid, DateTimeOffset, etc.
6. Base DbContext
Inherit from BaseDbContext for automatic logger injection and instance tracking.
using CSharpEssentials.EntityFrameworkCore;
public class MyDbContext : BaseDbContext<MyDbContext>
{
public MyDbContext(DbContextOptions<MyDbContext> options, IServiceScopeFactory scopeFactory)
: base(options, scopeFactory)
{
}
}
Full Setup Example
var services = new ServiceCollection();
services.AddLogging(b => b.AddConsole());
// Interceptors — one line each
services.AddAuditInterceptor(() => "demo-user");
services.AddSlowQueryInterceptor(TimeSpan.FromMilliseconds(500));
services.AddSingleton<IDomainEventPublisher, MyPublisher>();
services.AddSingleton<DomainEventInterceptor>();
// DbContext with all interceptors
services.AddDbContext<ShopDbContext>((sp, options) =>
options.UseSqlServer(connectionString)
.AddInterceptors(
sp.GetRequiredService<AuditInterceptor>(),
sp.GetRequiredService<DomainEventInterceptor>(),
sp.GetRequiredService<SlowQueryInterceptor>()));
| 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
- CSharpEssentials.Core (>= 3.0.2)
- CSharpEssentials.Entity (>= 3.0.2)
- CSharpEssentials.Enums (>= 3.0.2)
- CSharpEssentials.Json (>= 3.0.2)
- CSharpEssentials.Maybe (>= 3.0.2)
- CSharpEssentials.Results (>= 3.0.2)
- EFCore.NamingConventions (>= 9.0.0)
- Microsoft.EntityFrameworkCore (>= 9.0.4)
- Microsoft.Extensions.Caching.Memory (>= 9.0.4)
-
net8.0
- CSharpEssentials.Core (>= 3.0.2)
- CSharpEssentials.Entity (>= 3.0.2)
- CSharpEssentials.Enums (>= 3.0.2)
- CSharpEssentials.Json (>= 3.0.2)
- CSharpEssentials.Maybe (>= 3.0.2)
- CSharpEssentials.Results (>= 3.0.2)
- EFCore.NamingConventions (>= 8.0.0)
- Microsoft.Bcl.TimeProvider (>= 8.0.0)
- Microsoft.EntityFrameworkCore (>= 8.0.0)
- Microsoft.Extensions.Caching.Memory (>= 9.0.4)
- System.Text.Json (>= 9.0.0)
-
net9.0
- CSharpEssentials.Core (>= 3.0.2)
- CSharpEssentials.Entity (>= 3.0.2)
- CSharpEssentials.Enums (>= 3.0.2)
- CSharpEssentials.Json (>= 3.0.2)
- CSharpEssentials.Maybe (>= 3.0.2)
- CSharpEssentials.Results (>= 3.0.2)
- EFCore.NamingConventions (>= 9.0.0)
- Microsoft.EntityFrameworkCore (>= 9.0.4)
- Microsoft.Extensions.Caching.Memory (>= 9.0.4)
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 |
|---|---|---|
| 3.0.3 | 0 | 5/5/2026 |
| 3.0.2 | 13 | 5/5/2026 |
| 3.0.1 | 39 | 5/3/2026 |
| 3.0.0 | 38 | 5/3/2026 |
| 2.1.0 | 222 | 11/26/2025 |
| 2.0.9 | 218 | 9/30/2025 |
| 2.0.8 | 205 | 9/29/2025 |
| 2.0.7 | 205 | 9/29/2025 |
| 2.0.6 | 209 | 9/29/2025 |
| 2.0.5 | 208 | 9/29/2025 |
| 2.0.4 | 199 | 9/28/2025 |
| 2.0.3 | 202 | 9/28/2025 |
| 2.0.2 | 208 | 9/28/2025 |
| 2.0.1 | 199 | 9/28/2025 |
| 2.0.0 | 221 | 9/28/2025 |
| 1.0.16 | 489 | 2/4/2025 |
| 1.0.15 | 385 | 1/29/2025 |
| 1.0.14 | 184 | 12/27/2024 |
| 1.0.13 | 222 | 12/18/2024 |
| 1.0.12 | 213 | 12/18/2024 |