R8.EntityFrameworkCore.AuditProvider.Abstractions
1.2.0
dotnet add package R8.EntityFrameworkCore.AuditProvider.Abstractions --version 1.2.0
NuGet\Install-Package R8.EntityFrameworkCore.AuditProvider.Abstractions -Version 1.2.0
<PackageReference Include="R8.EntityFrameworkCore.AuditProvider.Abstractions" Version="1.2.0" />
<PackageVersion Include="R8.EntityFrameworkCore.AuditProvider.Abstractions" Version="1.2.0" />
<PackageReference Include="R8.EntityFrameworkCore.AuditProvider.Abstractions" />
paket add R8.EntityFrameworkCore.AuditProvider.Abstractions --version 1.2.0
#r "nuget: R8.EntityFrameworkCore.AuditProvider.Abstractions, 1.2.0"
#:package R8.EntityFrameworkCore.AuditProvider.Abstractions@1.2.0
#addin nuget:?package=R8.EntityFrameworkCore.AuditProvider.Abstractions&version=1.2.0
#tool nuget:?package=R8.EntityFrameworkCore.AuditProvider.Abstractions&version=1.2.0
R8.EntityFrameworkCore.AuditProvider
A .NET package for Entity Framework, providing comprehensive change tracking with deep insights. Capture creation, updates, deletions, and restorations of entities, including property names, old and new values, and user details, all neatly stored in an Audits column as JSON.
Seamless Entity Auditing: Easily integrate audit functionality into your Entity Framework applications, offering a complete audit trail enriched with user information. Gain full visibility into entity lifecycle changes for compliance, debugging, and accountability.
Full Entity Lifecycle Visibility: Track the complete life cycle of your entities with detailed auditing. For each change this package records the flag (created/changed/deleted/restored), the timestamp, the changed properties with their old and new values, and the user behind the action.
Targets net6.0, net8.0, and net10.0. The interceptor is registered as a thread-safe singleton, so a single registration is safe to share across all your DbContexts.
Installation
dotnet add package R8.EntityFrameworkCore.AuditProvider
Known Limitations
The interceptor can only detect changes on tracked entities. An entity loaded with .AsNoTracking() (or otherwise detached) has no tracked baseline to diff against. To audit an update to such an entity, Attach it first and then modify it, so EF captures the original values before the change.
Usage
// ... other services
// Add AuditProvider
services.AddEntityFrameworkAuditProvider(options =>
{
options.JsonOptions.WriteIndented = false;
options.AuditFlagSupport.Created = AuditFlagState.ActionDate | AuditFlagState.Storage;
options.AuditFlagSupport.Changed = AuditFlagState.ActionDate | AuditFlagState.Storage;
options.AuditFlagSupport.Deleted = AuditFlagState.ActionDate | AuditFlagState.Storage;
options.AuditFlagSupport.UnDeleted = AuditFlagState.ActionDate | AuditFlagState.Storage;
options.MaxStoredAudits = 10;
options.DateTimeProvider = serviceProvider => DateTime.UtcNow;
options.UserProvider = serviceProvider =>
{
var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
var user = httpContextAccessor.HttpContext?.User;
if (user?.Identity?.IsAuthenticated == true)
{
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
var username = user.FindFirstValue(ClaimTypes.Name);
return new AuditProviderUser(userId, new Dictionary<string, string>
{
{ "Username", username }
});
}
return null;
};
});
services.AddDbContext<YourDbContext>((serviceProvider, optionsBuilder) =>
{
// Your DbContext connection configuration here
// ...
optionsBuilder.AddEntityFrameworkAuditProviderInterceptor(serviceProvider);
});
Options
| Option | Type | Description | Default |
|---|---|---|---|
JsonOptions |
System.Text.Json.JsonSerializerOptions |
Json serializer options to serialize and deserialize audits | An optimal setting |
AuditFlagSupport |
R8.EntityFrameworkCore.AuditProvider.AuditProviderFlagSupport |
Audit flags to include | All flags are included |
MaxStoredAudits* |
int? |
Maximum number of audits to store in Audits column |
null |
DateTimeProvider |
Func<IServiceProvider, DateTime> |
DateTime provider to get current date time | DateTime.UtcNow |
UserProvider |
Func<IServiceProvider, AuditProviderUser?> |
User provider to get current user id | null |
- If the number of audits exceeds this number, the earliest audits (except
Created) will be removed from the column. Ifnull, all audits will be stored.
Wiki
IAuditActivatorinterface: to start auditing entities.IAuditJsonStorageinterface: to store audits in a single JSON column (JsonElement? Audits; e.g.jsonbon PostgreSQL,nvarchar(max)on SQL Server).IAuditStorageinterface: to store audits as anAudit[]? Auditscolumn (serialize/deserialize withAuditProviderConfiguration.JsonOptions).IAuditSoftDeleteinterface: to soft-delete entities.IAuditCreateDateinterface: to store creation date in a column.IAuditUpdateDateinterface: to store last update/restore date in a column.IAuditDeleteDateinterface: to store deletion date in a column.[AuditIgnore]attribute: to ignore a property from audit.
Samples:
PostgreSQL: AggregateAuditable.csMicrosoft Sql Server: AggregateAuditable.cs- or as below (for
PostgreSQL):
public record YourEntity : IAuditActivator, IAuditJsonStorage, IAuditSoftDelete, IAuditCreateDate, IAuditUpdateDate, IAuditDeleteDate
{
[Key]
public int Id { get; set; }
[Column(TypeName = "jsonb"), AuditIgnore]
public JsonElement? Audits { get; set; }
public bool IsDeleted { get; set; }
[Column("CreatedAt", TypeName = "timestamp")]
public DateTime? CreateDate { get; set; }
[Column("UpdatedAt", TypeName = "timestamp")]
public DateTime? UpdateDate { get; set; }
[Column("DeletedAt", TypeName = "timestamp")]
public DateTime? DeleteDate { get; set; }
// ...
// public string Name { get; set; }
// public string Description { get; set; }
// etc.
}
Migration
Highly recommended to test it on a test database first, to avoid any data loss.
Considerations
- Since
Microsoft Sql Serverdoes not supportjsontype,Auditscolumn will be stored asnvarchar(max)andJsonElementwill be serialized/deserialized to/fromstring. (See AggregateAuditable.cs) - The key to allow auditing entities is implementation of
IAuditActivatorto your entity.- the
IAuditStorage,IAuditSoftDelete,IAuditCreateDate,IAuditUpdateDate, andIAuditDeleteDateinterfaces takes effect only ifIAuditActivatoris implemented to entity. If not implemented, the entity will be updated with the properSaveChanges/SaveChangesAsyncfunctionality inEntity Framework Core.
- the
DeletedandUnDeletedflag cannot be stored simultaneously withCreatedandChangedflags.- If
IAuditStorageorIAuditJsonStorageis implemented to your entity, theAuditscolumn will be stored in the specified table. - If any of
IAuditCreateDate,IAuditUpdateDateorIAuditDeleteDateis implemented to entity, the corresponding date will be stored on its own column alongside theAuditsupdate. - Any support flag in
AuditProviderOptions.AuditFlagSupportmust be written as a flag:AuditFlagState.ActionDate | AuditFlagState.Storage- If any of
AuditFlagenums are included/excluded fromAuditFlagSupport, the corresponding flag will take action inAuditsand/or{Action}Datecolumn according to the its state inAuditFlagSupport. (For instance, ifAuditFlagSupport.Created = AuditFlagState.Excluded,IAuditCreateDateandIAuditStorage, also andCreatedflag will be ignored.)
- If any of
Audit Collection
To take advantages of JsonElement Audits (as a property in the IAuditJsonStorage interface):
var entity = await dbContext.YourEntities.FindAsync(1);
var audits = entity.GetAuditCollection();
Audit[] deserializedAudits = audits.ToArray(); // Get audits as array
Audit creationAudit = audits.First(); // Get created audit
Audit lastAudit = audits.Last(false); // Get last audit. (false) means to exclude Deleted flag audit, if is the last one.
Audit[] changes = audits.Track(nameof(entity.Name)); // Get changes of a property
Output Example
Stored data in Audits column will be like this:
[
{
"f": 0, // Created
"dt": "2023-09-25T12:00:00.0000000+03:30", // Date and time of the action
},
{
"f": 1, // Changed
"dt": "2023-09-25T12:00:00.0000000+03:30", // Date and time of the action
"c": [ // Changes
{
"n": "Name", // Name of the property
"_v": "OldName", // Old value
"v": "NewName" // New value
},
{
"n": "Age", // Name of the property
"_v": 0, // Old value
"v": 33 // New value
}
],
"u": { // User that made the change
"id": "1", // The user id (if provided)
"ad": { // The user additional info (if provided)
"Username": "Foo"
}
}
},
{
"f": 2, // Deleted
"dt": "2023-09-25T12:00:00.0000000+03:30", // Date and time of the action
},
{
"f": 3, // Restored/Undeleted
"dt": "2023-09-25T12:00:00.0000000+03:30", // Date and time of the action
}
]
🎆 Happy coding!
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 was computed. 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
- No dependencies.
-
net6.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on R8.EntityFrameworkCore.AuditProvider.Abstractions:
| Package | Downloads |
|---|---|
|
R8.EntityFrameworkCore.AuditProvider
A .NET package for Entity Framework, providing comprehensive change tracking with deep insights. |
GitHub repositories
This package is not used by any popular GitHub repositories.