Reputation: 41
I'm creating a change log on my records and ChangeTracking only reads records from the main class.
I've already added slow loading and the child classes are being loaded inside my EntityEntry, but I can't capture them through entry.Properties
My class
private void OnBeforeSaveChanges()
{
ChangeTracker.DetectChanges();
var auditEntries = new List<AuditEntry>();
foreach (var entry in ChangeTracker.Entries())
{
if (entry.Entity is AuditLog || entry.State == EntityState.Detached || entry.State == EntityState.Unchanged)
continue;
var auditEntry = new AuditEntry(entry)
{
TableName = entry.Entity.GetType().Name,
UserId = _aplicationUser.GetId
};
auditEntries.Add(auditEntry);
foreach (var property in entry.Properties)
{
string propertyName = property.Metadata.Name;
if (property.Metadata.IsPrimaryKey())
{
auditEntry.KeyValues[propertyName] = property.CurrentValue;
continue;
}
switch (entry.State)
{
case EntityState.Added:
auditEntry.AuditType = AuditType.Create;
auditEntry.NewValues[propertyName] = property.CurrentValue;
break;
case EntityState.Deleted:
auditEntry.AuditType = AuditType.Delete;
auditEntry.OldValues[propertyName] = property.OriginalValue;
break;
case EntityState.Modified:
if (property.IsModified)
{
auditEntry.ChangedColumns.Add(propertyName);
auditEntry.AuditType = AuditType.Update;
auditEntry.OldValues[propertyName] = property.OriginalValue;
auditEntry.NewValues[propertyName] = property.CurrentValue;
}
break;
}
}
}
foreach (var auditEntry in auditEntries)
{
var auditDto = auditEntry.ToAudit();
AuditLogs.Add(new AuditLog(auditDto.Id, auditDto.UserId, auditDto.Type, auditDto.TableName, auditDto.DateTime,
auditDto.OldValues, auditDto.NewValues, auditDto.AffectedColumns, auditDto.PrimaryKey));
}
}
Upvotes: 1
Views: 1739
Reputation: 34653
The change tracker reports on a per-entity basis. If you have a Parent entity and a Child collection beneath it and make a change to the parent and one or more children, the parent only shows up in the change tracker because of the changes to the parent's properties. It does not list the properties of all related / child entities. Any modified children would appear in the ChangeTracker as Child entities.
For example:
var parent = context.Parents.Include(x => x.Children).Single(x => x.Id == parentId);
var oldestChild = parent.Children.OrderByDescending(x => x.Age).FirstOrDefault();
parent.SomeValue = newValue;
if (oldestChild != null)
oldestChild.OtherValue = someOtherValue;
context.SaveChanges();
In this case the change tracker would include Parent if, and only if the newValue differed from the existing SomeValue. The change tracker would also include the Child denoted as the oldestChild
if such a child actually exists, and the someOtherValue differed from that child's OtherValue. If the Child's value was the only one that actually changed then the Change tracker would not include that child's Parent, only the Child reference. This means you cannot really capture an easy "complete" before and after snapshot of an entire object graph. (Parent and all of it's children before and after, or including the parent when one of it's children happens to change) You can do it, but it will involve piecing the deltas together based on what's in the change tracker.
When it comes to writing audit records I would suggest using a bounded DbContext solely for this purpose that only manages the audit log entities and keeps the entity structure as simple as absolutely possible. This way main app context can intercept and inspect changes without worrying about audit changes, and the audit DbContext avoids this overhead, just performing the raw writes.
Upvotes: 1