Linger.EFCore.Audit
1.4.2
dotnet add package Linger.EFCore.Audit --version 1.4.2
NuGet\Install-Package Linger.EFCore.Audit -Version 1.4.2
<PackageReference Include="Linger.EFCore.Audit" Version="1.4.2" />
<PackageVersion Include="Linger.EFCore.Audit" Version="1.4.2" />
<PackageReference Include="Linger.EFCore.Audit" />
paket add Linger.EFCore.Audit --version 1.4.2
#r "nuget: Linger.EFCore.Audit, 1.4.2"
#:package Linger.EFCore.Audit@1.4.2
#addin nuget:?package=Linger.EFCore.Audit&version=1.4.2
#tool nuget:?package=Linger.EFCore.Audit&version=1.4.2
Linger.EFCore.Audit
An Entity Framework Core audit trail library for automatically tracking data changes.
✨ Features
- Automatic audit logging for Entity Framework Core operations
- Tracks entity creation, modification, and deletion
- Captures old and new values for changed properties
- Records user information for each change
- Supports soft delete
- Built-in JSON serialization for audit data
- Compatible with EF Core 9.0 and 8.0
📦 Installation
From Visual Studio
- Open the
Solution Explorer. - Right-click on a project within your solution.
- Click on
Manage NuGet Packages.... - Click on the
Browsetab and search for "Linger.EFCore.Audit". - Click on the
Linger.EFCore.Auditpackage, select the appropriate version and click Install.
Package Manager Console
PM> Install-Package Linger.EFCore.Audit
.NET CLI Console
> dotnet add package Linger.EFCore.Audit
🚀 Quick Start
Configuration
Add the audit functionality to your EF Core DbContext:
// 1. Add audit trail to your DbContext
public class AppDbContext : DbContext
{
public DbSet<AuditTrailEntry> AuditTrails { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Apply audit configurations
modelBuilder.ApplyAudit();
}
}
// 2. Implement audit user provider
public class CurrentUserProvider : IAuditUserProvider
{
// Can get user info from your authentication system
public string? UserName => "john.doe";
public string GetUser() => UserName ?? "anonymous";
}
// 3. Register services and interceptor
services.AddScoped<IAuditUserProvider, CurrentUserProvider>();
services.AddDbContext<AppDbContext>((serviceProvider, options) =>
{
options.UseSqlServer(connectionString);
// Without logging
options.AddInterceptors(
new AuditEntitiesSaveChangesInterceptor(
serviceProvider.GetRequiredService<IAuditUserProvider>()
)
);
// Or with logging (Recommended - for monitoring and debugging)
options.AddInterceptors(
new AuditEntitiesSaveChangesInterceptor(
serviceProvider.GetRequiredService<IAuditUserProvider>(),
serviceProvider.GetRequiredService<ILogger<AuditEntitiesSaveChangesInterceptor>>()
)
);
});
Example Usage
Automatic tracking of all changes to entities:
// Create a new entity
var user = new User
{
Name = "John Doe",
Email = "john.doe@example.com"
};
dbContext.Users.Add(user);
await dbContext.SaveChangesAsync(); // Automatically generates "Created" audit record with user info
// Modify entity
user.Email = "new.email@example.com";
await dbContext.SaveChangesAsync(); // Automatically generates "Modified" audit record
// Delete entity
dbContext.Users.Remove(user);
await dbContext.SaveChangesAsync(); // Automatically generates "Deleted" audit record (soft delete)
Querying Audit Records
Audit records are stored in the AuditTrails DbSet and can be queried in various ways:
// Find all audit records related to a specific entity
var entityAudits = await dbContext.AuditTrails
.Where(a => a.EntityId == "123" && a.EntityName == "User")
.OrderBy(a => a.TimeStamp)
.ToListAsync();
// Display audit history
foreach (var audit in entityAudits)
{
Console.WriteLine($"Action: {audit.AuditType}, Time: {audit.TimeStamp}, User: {audit.Username}");
// Display all changed properties
if (audit.AffectedColumns != null)
{
Console.WriteLine("Changes:");
foreach (var column in audit.AffectedColumns)
{
var oldValue = audit.OldValues?[column];
var newValue = audit.NewValues?[column];
Console.WriteLine($" {column}: Old = {oldValue}, New = {newValue}");
}
}
}
// Query audit records by user
var userAudits = await dbContext.AuditTrails
.Where(a => a.Username == "john.doe")
.OrderByDescending(a => a.TimeStamp)
.Take(10)
.ToListAsync();
// Query audit records within a specific date range
var startDate = DateTimeOffset.Now.AddDays(-7);
var endDate = DateTimeOffset.Now;
var recentAudits = await dbContext.AuditTrails
.Where(a => a.TimeStamp >= startDate && a.TimeStamp <= endDate)
.OrderBy(a => a.TimeStamp)
.ToListAsync();
📄 Audit Trail Data
The AuditTrailEntry class is the main class that represents a single audit record:
public class AuditTrailEntry
{
public long Id { get; set; }
public string? Username { get; set; }
public AuditType AuditType { get; set; } // Added, Modified or Deleted
public string EntityName { get; set; }
public string? EntityId { get; set; }
public Dictionary<string, object>? OldValues { get; set; }
public Dictionary<string, object>? NewValues { get; set; }
public List<string>? AffectedColumns { get; set; }
public DateTimeOffset TimeStamp { get; set; }
public Dictionary<string, object>? Changes { get; set; }
public IEnumerable<PropertyEntry>? TempProperties { get; set; }
}
The AuditTrailEntry captures:
- Entity name and ID
- Type of change (Added/Modified/Deleted)
- Username performing the change
- Timestamp
- Old and new property values
- List of modified columns
🔍 Automatic Tracking
- Creation audit: CreatorId, CreationTime
- Modification audit: LastModifierId, LastModificationTime
- Soft delete: IsDeleted, DeleterId, DeletionTime
📊 Logging
The interceptor supports optional logging to help monitor audit operations and troubleshoot issues.
Configure Log Level
In appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Linger.EFCore.Audit.Interceptors": "Debug"
}
}
}
| 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
- Linger.Audit (>= 1.4.2)
- Linger.EFCore (>= 1.4.2)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.8)
-
net8.0
- Linger.Audit (>= 1.4.2)
- Linger.EFCore (>= 1.4.2)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.26)
-
net9.0
- Linger.Audit (>= 1.4.2)
- Linger.EFCore (>= 1.4.2)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.15)
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 |
|---|---|---|
| 1.4.2 | 93 | 5/20/2026 |
| 1.4.1-preview | 88 | 5/12/2026 |
| 1.4.0 | 93 | 5/6/2026 |
| 1.3.3-preview | 84 | 5/5/2026 |
| 1.3.2-preview | 90 | 4/29/2026 |
| 1.3.1-preview | 95 | 4/28/2026 |
| 1.3.0-preview | 94 | 4/27/2026 |
| 1.2.0-preview | 104 | 3/29/2026 |
| 1.1.0 | 127 | 2/4/2026 |
| 1.0.3-preview | 117 | 1/9/2026 |
| 1.0.2-preview | 117 | 1/8/2026 |
| 1.0.0 | 313 | 11/12/2025 |
| 1.0.0-preview2 | 179 | 11/6/2025 |
| 1.0.0-preview1 | 183 | 11/5/2025 |
| 0.9.9 | 170 | 10/16/2025 |
| 0.9.8 | 182 | 10/14/2025 |
| 0.9.7-preview | 170 | 10/13/2025 |
| 0.9.6-preview | 149 | 10/12/2025 |
| 0.9.5 | 150 | 9/28/2025 |
| 0.9.4-preview | 173 | 9/25/2025 |