Ответ 1
Очень, очень грубая идея:
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 мог бы стать кошмаром производительности, хотя.
Edit
Возможно, лучше получить доступ к базовому 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
(который является ключом, чтобы избежать кода отражения) в API DbContext
.
Изменить 2
Важно. При работе с сущностями POCO код, указанный выше, должен вызвать DbContext.ChangeTracker.DetectChanges()
в начале. Причина в том, что base.SaveChanges
называется слишком поздно здесь (в конце метода). base.SaveChanges
вызывает DetectChanges
внутренне, но поскольку мы хотим проанализировать и зарегистрировать изменения раньше, мы должны вызвать DetectChanges
вручную, чтобы EF мог найти все измененные свойства и правильно установить состояния в трекере изменения.
Возможны ситуации, когда код может работать без вызова DetectChanges
, например, если методы DbContext/DbSet, такие как Add
или Remove
, используются после внесения последних изменений свойств, так как эти методы также вызывают DetectChanges
внутренне. Но если, например, объект просто загружен из БД, изменяется несколько свойств, и затем вызывается этот производный SaveChanges
, автоматическое обнаружение изменений не произойдет до base.SaveChanges
, что в конечном итоге приведет к отсутствию записей журнала для модифицированных свойств.
Я обновил приведенный выше код.