/// <summary> /// Sets up Entity Framework auditing and registers any subscribers /// </summary> public static IServiceCollection RegisterEntityFrameworkAuditing(this IServiceCollection services, ServiceLifetime serviceLifetime, Action <AuditOptions> configureOptions = null) { return(services.RegisterEntityFrameworkAuditing(serviceLifetime, provider => { var options = new AuditOptions(); configureOptions?.Invoke(options); return options; })); }
/// <summary> /// Sets up Entity Framework auditing and registers any subscribers /// </summary> public static IServiceCollection RegisterEntityFrameworkAuditing <TDbContext>(this IServiceCollection services, ServiceLifetime serviceLifetime, Action <AuditOptions> configureOptions = null, Func <IServiceProvider, IPostSaveAction <TDbContext> > postSaveAction = null) { return(services.RegisterEntityFrameworkAuditing(serviceLifetime, provider => { var options = new AuditOptions(); configureOptions?.Invoke(options); return options; }, postSaveAction)); }
private static List <AuditEntry> OnBeforeSaveChanges(this DbContext context, AuditOptions options) { context.ChangeTracker.DetectChanges(); var auditEntries = new List <AuditEntry>(); foreach (var entry in context.ChangeTracker.Entries()) { if (entry.State == EntityState.Detached || entry.State == EntityState.Unchanged) { continue; } var auditEntry = new AuditEntry(entry) { TableName = entry.Metadata.GetTableName(), CreateBy = options.User(), Client = options.Client(), CreateDate = options.CurrentDateTime(), Action = entry.State.ToString() }; auditEntries.Add(auditEntry); foreach (var property in entry.Properties) { if (property.IsTemporary) { // value will be generated by the database, get the value after saving auditEntry.TemporaryProperties.Add(property); continue; } var propertyName = property.Metadata.GetColumnName(); if (property.Metadata.IsPrimaryKey()) { auditEntry.KeyValues[propertyName] = property.CurrentValue; continue; } switch (entry.State) { case EntityState.Added: auditEntry.Changes[propertyName] = new AuditEntry.Change { NewValue = property.CurrentValue }; break; case EntityState.Deleted: auditEntry.Changes[propertyName] = new AuditEntry.Change { OldValue = property.OriginalValue }; break; case EntityState.Modified: if (property.IsModified) { auditEntry.Changes[propertyName] = new AuditEntry.Change { OldValue = property.OriginalValue, NewValue = property.CurrentValue } } ; break; } } } return(auditEntries.ToList()); }
private static async Task <List <Audit> > OnAfterSaveChanges(this DbContext context, List <AuditEntry> auditEntries, AuditOptions options) { if (auditEntries == null || auditEntries.Count == 0) { return(new List <Audit>()); } var transactionId = options.TransactionId(); var serializerSettings = options.JsonSerializerSettings; var result = new List <EntityEntry <Audit> >(); foreach (var auditEntry in auditEntries) { // Get the final value of the temporary properties foreach (var prop in auditEntry.TemporaryProperties) { if (prop.Metadata.IsPrimaryKey()) { auditEntry.KeyValues[prop.Metadata.GetColumnName()] = prop.CurrentValue; } else { auditEntry.Changes[prop.Metadata.GetColumnName()] = new AuditEntry.Change { NewValue = prop.CurrentValue } } } } ; // Group together audit changes for the same table/key combination (e.g. owned entities which initially come through as distinct changes) var grouped = auditEntries.GroupBy(x => new { x.TableName, KeyValues = JsonConvert.SerializeObject(x.KeyValues, serializerSettings), x.Action, x.Client, x.CreateBy, x.CreateDate }); foreach (var group in grouped) { if (group.Count() > 1) { result.Add(context.Set <Audit>().Add(new Audit { TableName = group.Key.TableName, Action = group.Key.Action, Client = group.Key.Client, CreateDate = group.Key.CreateDate, CreatedBy = group.Key.CreateBy, RowId = group.Key.KeyValues, Data = JsonConvert.SerializeObject( group.SelectMany(x => x.Changes).ToDictionary(pair => pair.Key, pair => pair.Value), serializerSettings), TransactionId = transactionId })); } else { // Save the Audit entry result.Add(context.Set <Audit>() .Add(group.First().ToAuditItem(transactionId, serializerSettings))); } } await context.SaveChangesAsync(); return(result.Select(x => x.Entity).ToList()); }