private static void CompleteAudit(DbContext context) { var entries = context.ChangeTracker.Entries() .Where(x => new[] { EntityState.Added, EntityState.Modified, EntityState.Deleted }.Contains(x.State)) .ToArray(); var predictor = new AuditPredictor(); var auditEntriesByTypes = entries .GroupBy(x => x.Entity.GetType()) .Where(x => x.Key.HasAttribute <EntityAuditAttribute>()) .ToArray(); // Complete EntityAudit foreach (var entriesByType in auditEntriesByTypes) { var attr = entriesByType.Key.GetCustomAttribute <EntityAuditAttribute>(); foreach (var kv in entriesByType.AsKvPairs()) { predictor.Add(EntityAudit.Parse(kv.Value)); } } foreach (var entriesByType in auditEntriesByTypes) { var entityType = entriesByType.Key; var attr = entityType.GetCustomAttribute <EntityAuditAttribute>(); var auditor = Activator.CreateInstance(attr.EntityAuditorType); auditor.GetReflector().DeclaredMethod(nameof(IEntityAuditor <DbContext, object> .OnAudited)).Call(context, predictor); } }
/// <summary> /// This method should be called before 'base.SaveChanges'. /// </summary> /// <param name="context"></param> /// <param name="acceptAllChangesOnSuccess"></param> /// <returns></returns> public static void IntelliTrack(DbContext context, bool acceptAllChangesOnSuccess) { EntityEntry[] entries; IGrouping <Type, EntityEntry>[] auditEntriesByTypes; void RefreshEntries() { entries = context.ChangeTracker.Entries() .Where(x => new[] { EntityState.Added, EntityState.Modified, EntityState.Deleted }.Contains(x.State)) .ToArray(); auditEntriesByTypes = entries .GroupBy(x => x.Entity.GetType()) .Where(x => x.Key.HasAttribute <EntityAuditAttribute>()) .ToArray(); } var auditorCaches = new CacheContainer <Type, Reflector> { CacheMethod = auditType => () => Activator.CreateInstance(auditType).GetReflector(), }; RefreshEntries(); foreach (var entry in entries) { // Resolve AutoAttributes var entity = entry.Entity; var entityType = entity.GetType(); if (entry.State == EntityState.Added || entry.State == EntityState.Modified) { var props = entityType.GetProperties().Where(x => x.CanWrite).ToArray(); ResolveAutoAttributes(entry, props); } } // Resolve BeforeAudit foreach (var entriesByType in auditEntriesByTypes) { var entityType = entriesByType.Key; var attr = entityType.GetCustomAttribute <EntityAuditAttribute>(); var auditType = typeof(EntityAudit <>).MakeGenericType(entityType); var audits = Array.CreateInstance(auditType, entriesByType.Count()); foreach (var kv in entriesByType.AsKvPairs()) { audits.SetValue(EntityAudit.Parse(kv.Value), kv.Key); } auditorCaches[attr.EntityAuditorType].Value .DeclaredMethod(nameof(IEntityAuditor <DbContext, object> .BeforeAudit)).Call(context, audits); } // Resolve OnAuditing RefreshEntries(); foreach (var entriesByType in auditEntriesByTypes) { var entityType = entriesByType.Key; var attr = entityType.GetCustomAttribute <EntityAuditAttribute>(); var auditType = typeof(EntityAudit <>).MakeGenericType(entityType); var audits = Array.CreateInstance(auditType, entriesByType.Count()); foreach (var kv in entriesByType.AsKvPairs()) { audits.SetValue(EntityAudit.Parse(kv.Value), kv.Key); } auditorCaches[attr.EntityAuditorType].Value .DeclaredMethod(nameof(IEntityAuditor <DbContext, object> .OnAuditing)).Call(context, audits); } // Resolve OnAudited CompleteAudit(context); }