Очень, очень грубая идея:
foreach (var property in dbEntityEntry.Entity.GetType().GetProperties())
{
DbPropertyEntry propertyEntry = dbEntityEntry.Property(property.Name);
if (propertyEntry.IsModified)
{
Log.WriteAudit("Entry: {0} Original :{1} New: {2}", property.Name,
propertyEntry.OriginalValue, propertyEntry.CurrentValue);
}
}
Понятия не имею, будет ли это действительно работать в деталях, но я бы попробовал это как первый шаг. Конечно, может быть более одного свойства, которое изменилось, поэтому цикл и, возможно, несколько вызовов WriteAudit
.
Отражение внутри SaveChanges может стать кошмаром производительности.
Редактировать
Возможно, лучше получить доступ к базовому ObjectContext
. Тогда что-то подобное возможно:
public class TestContext : DbContext
{
public override int SaveChanges()
{
ChangeTracker.DetectChanges(); // Important!
ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext;
List<ObjectStateEntry> objectStateEntryList =
ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Modified
| EntityState.Deleted)
.ToList();
foreach (ObjectStateEntry entry in objectStateEntryList)
{
if (!entry.IsRelationship)
{
switch (entry.State)
{
case EntityState.Added:
// write log...
break;
case EntityState.Deleted:
// write log...
break;
case EntityState.Modified:
{
foreach (string propertyName in
entry.GetModifiedProperties())
{
DbDataRecord original = entry.OriginalValues;
string oldValue = original.GetValue(
original.GetOrdinal(propertyName))
.ToString();
CurrentValueRecord current = entry.CurrentValues;
string newValue = current.GetValue(
current.GetOrdinal(propertyName))
.ToString();
if (oldValue != newValue) // probably not necessary
{
Log.WriteAudit(
"Entry: {0} Original :{1} New: {2}",
entry.Entity.GetType().Name,
oldValue, newValue);
}
}
break;
}
}
}
}
return base.SaveChanges();
}
}
Я сам использовал это в EF 4.0. Я не могу найти соответствующий метод для GetModifiedProperties
(который является ключом, чтобы избежать кода отражения) в DbContext
API.
Редактировать 2
Важно : При работе с объектами POCO приведенный выше код должен вызывать DbContext.ChangeTracker.DetectChanges()
в начале. Причина в том, что base.SaveChanges
вызывается здесь слишком поздно (в конце метода). base.SaveChanges
вызывает DetectChanges
внутри, но поскольку мы хотим проанализировать и записать изменения раньше, мы должны вызвать DetectChanges
вручную, чтобы EF мог найти все измененные свойства и правильно установить состояния в трекере изменений.
Возможны ситуации, когда код может работать без вызова DetectChanges
, например, если методы DbContext / DbSet, такие как Add
или Remove
, используются после последних изменений свойства, поскольку эти методы также вызывают DetectChanges
внутренне. Но если, например, сущность только что загружена из БД, несколько свойств изменяются, и затем вызывается этот производный SaveChanges
, автоматическое обнаружение изменений не произойдет до base.SaveChanges
, что в итоге приведет к отсутствию записей журнала для измененных свойств.
Я обновил код выше соответственно.