Une idée très, très sommaire :
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);
}
}
Je n'ai aucune idée si cela fonctionnerait vraiment dans le détail, mais c'est quelque chose que j'essaierais dans un premier temps. Bien sûr, il pourrait y avoir plus d'une propriété qui a changé, donc la boucle et peut-être plusieurs appels de WriteAudit
.
Le truc de réflexion à l'intérieur de SaveChanges pourrait devenir un cauchemar de performance cependant.
Modifier
Il est peut-être préférable d'accéder au sous-jacent ObjectContext
. Alors quelque chose comme ceci est possible :
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();
}
}
Je l'ai moi-même utilisé dans EF 4.0. Je ne trouve pas de méthode correspondante à GetModifiedProperties
(ce qui est la clé pour éviter le code de réflexion) dans le fichier DbContext
API.
Edit 2
Important : Lorsque l'on travaille avec des entités POCO, le code ci-dessus doit appeler DbContext.ChangeTracker.DetectChanges()
au début. La raison en est que base.SaveChanges
est appelé trop tard ici (à la fin de la méthode). base.SaveChanges
appelle DetectChanges
en interne, mais parce que nous voulons analyser et enregistrer les changements avant, nous devons appeler DetectChanges
manuellement afin que EF puisse trouver toutes les propriétés modifiées et définir correctement les états dans le suivi des modifications.
Il y a des situations possibles où le code peut fonctionner sans appeler DetectChanges
Par exemple, si des méthodes DbContext/DbSet telles que Add
o Remove
sont utilisées après les dernières modifications de propriétés puisque ces méthodes appellent également DetectChanges
en interne. Mais si, par exemple, une entité est simplement chargée à partir de la base de données, que quelques propriétés sont modifiées et qu'ensuite cette dérivée SaveChanges
est appelé, la détection automatique des changements ne se ferait pas avant que base.SaveChanges
Il en résulte finalement des entrées de journal manquantes pour les propriétés modifiées.
J'ai mis à jour le code ci-dessus en conséquence.