![]() |
VOOZH | about |
dotnet add package DKNet.EfCore.AuditLogs --version 10.0.27
NuGet\Install-Package DKNet.EfCore.AuditLogs -Version 10.0.27
<PackageReference Include="DKNet.EfCore.AuditLogs" Version="10.0.27" />
<PackageVersion Include="DKNet.EfCore.AuditLogs" Version="10.0.27" />Directory.Packages.props
<PackageReference Include="DKNet.EfCore.AuditLogs" />Project file
paket add DKNet.EfCore.AuditLogs --version 10.0.27
#r "nuget: DKNet.EfCore.AuditLogs, 10.0.27"
#:package DKNet.EfCore.AuditLogs@10.0.27
#addin nuget:?package=DKNet.EfCore.AuditLogs&version=10.0.27Install as a Cake Addin
#tool nuget:?package=DKNet.EfCore.AuditLogs&version=10.0.27Install as a Cake Tool
Structured, fire-and-forget audit logging for Entity Framework Core using a lightweight hook/interceptor pipeline.
Most EF Core audit samples depend on change trackers at save time but return raw debug views or force synchronous post-processing. This library:
Implement IAuditedProperties directly or inherit from AuditedEntity / AuditedEntity<TKey> which provides:
CreatedBy, CreatedOn (set once via SetCreatedBy – idempotent)UpdatedBy, UpdatedOn (set via SetUpdatedBy on each change)LastModifiedBy, LastModifiedOnOnly entities implementing IAuditedProperties and whose original state (snapshot) is Modified or Deleted produce
audit logs.
EfCoreAuditLog contains:
CreatedBy, CreatedOn, UpdatedBy, UpdatedOnEntityName – CLR type nameChanges – IReadOnlyList<EfCoreAuditFieldChange>
FieldName, OldValue, NewValueNewValue is always null (entity removed)ChangedView (obsolete legacy long EF debug view retained for backward compatibility – prefer Changes)After a successful SaveChanges the hook:
BeforeSaveAsync snapshot.AfterSaveAsync.ILogger<EfCoreAuditHook>). Your application code will
not fail because a publisher failed.Implement:
public interface IAuditLogPublisher
{
Task PublishAsync(IEnumerable<EfCoreAuditLog> logs, CancellationToken cancellationToken = default);
}
Register one or many. They are resolved every save; make them lightweight and thread-safe. Recommended: Singleton services storing or forwarding logs in a concurrent collection or an async queue.
NuGet (adjust version as needed):
dotnet add package DKNet.EfCore.AuditLogs
This library also requires:
dotnet add package Microsoft.EntityFrameworkCore
(Your DbContext provider – e.g. SqlServer / Sqlite / Npgsql – must also be installed.)
// 1. Define an audited entity
public sealed class Order : AuditedEntity<Guid>
{
public required string Number { get; set; }
public decimal Total { get; set; }
}
// 2. Custom publisher
public sealed class ConsoleAuditPublisher : IAuditLogPublisher
{
public Task PublishAsync(IEnumerable<EfCoreAuditLog> logs, CancellationToken ct = default)
{
foreach (var log in logs)
{
Console.WriteLine($"[AUDIT] {log.EntityName} by {log.LastActor()} changes: " +
string.Join(", ", log.Changes.Select(c => $"{c.FieldName}:{c.OldValue}->{c.NewValue}")));
}
return Task.CompletedTask;
}
}
// 3. Registration (Program.cs / Startup)
services
.AddLogging() // optional for error logging
.AddEfCoreAuditLogs<MyDbContext, ConsoleAuditPublisher>() // registers hook + publisher
.AddDbContextWithHook<MyDbContext>((sp, opt) =>
{
opt.UseSqlite("Data Source=app.db");
opt.EnableSensitiveDataLogging(); // helps diff; toggle for prod
});
// 4. Use your entity normally
var order = new Order { Number = "INV-100", Total = 125m };
order.SetCreatedBy(userName);
ctx.Add(order); // Create - no audit log produced
await ctx.SaveChangesAsync();
order.Total = 150m;
order.SetUpdatedBy(userName); // or a domain helper method
await ctx.SaveChangesAsync(); // Audit log emitted
Helper extension for last actor (illustrative):
public static class AuditLogExtensions
{
public static string LastActor(this EfCoreAuditLog log) => log.UpdatedBy ?? log.CreatedBy;
}
| Scenario | Method |
|---|---|
| Only hook (publishers registered separately) | services.AddEfCoreAuditLogs<TDbContext>() |
| Hook + single publisher | services.AddEfCoreAuditLogs<TDbContext, TPublisher>() |
| Multiple publishers | Call AddEfCoreAuditLogs<TDbContext>() then individual services.AddSingleton<IAuditLogPublisher, XPublisher>() |
IsModified or value inequality).NewValue = null.CreatedBy after initial set is ignored (idempotent). Ensure you call SetCreatedBy once.ILogger<EfCoreAuditHook> is registered). Processing continues
silently.BeforeSaveAsync captures snapshots before EF changes states.System.Threading.Channels) for buffering.Wrap or fork BuildAuditLog (internal extension) to exclude sensitive fields (like passwords, secrets). Provide a
custom hook variant if needed.
Add correlation IDs, tenant IDs, etc. via a decorator publisher or by extending your entity base with additional properties.
publisher.Received.Count >= expected) because publishing is asynchronous.ChangedView is marked [Obsolete] and will be removed in a future major version. Migrate to the structured
Changes collection.public class SerilogAuditPublisher : IAuditLogPublisher
{
public Task PublishAsync(IEnumerable<EfCoreAuditLog> logs, CancellationToken ct = default)
{
foreach (var log in logs)
{
Log.ForContext("entity", log.EntityName)
.ForContext("createdBy", log.CreatedBy)
.ForContext("updatedBy", log.UpdatedBy)
.ForContext("changes", log.Changes.Select(c => new { c.FieldName, c.OldValue, c.NewValue }))
.Information("EF audit");
}
return Task.CompletedTask;
}
}
Remove the hook registration or publisher registrations:
// Remove: services.AddEfCoreAuditLogs<MyDbContext>();
// Replace with normal AddDbContext if audit no longer needed.
Choose a suitable OSS license (e.g. MIT) – add LICENSE file at root.
Open an issue in the repository or integrate with your organization’s internal support channel.
TL;DR: Register the hook, mark entities as audited, implement publishers, get structured async audit logs with minimal impact on save performance.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.27 | 126 | 5/22/2026 |
| 10.0.26 | 97 | 5/19/2026 |
| 10.0.25 | 315 | 3/27/2026 |
| 10.0.24 | 110 | 3/27/2026 |
| 10.0.23 | 115 | 3/27/2026 |
| 10.0.22 | 108 | 3/26/2026 |
| 10.0.21 | 138 | 3/17/2026 |
| 10.0.20 | 119 | 2/2/2026 |
| 10.0.19 | 253 | 1/21/2026 |
| 10.0.18 | 125 | 1/21/2026 |
| 10.0.17 | 148 | 1/19/2026 |
| 10.0.16 | 123 | 1/18/2026 |
| 10.0.15 | 134 | 1/18/2026 |
| 10.0.14 | 122 | 1/18/2026 |
| 10.0.13 | 123 | 1/17/2026 |
| 10.0.12 | 115 | 1/17/2026 |
| 10.0.11 | 130 | 1/17/2026 |
| 10.0.10 | 123 | 1/17/2026 |
| 10.0.9 | 127 | 1/16/2026 |
| 10.0.8 | 123 | 1/16/2026 |