Ottenere tutte le modifiche apportate a un object in Entity Framework

Esiste un modo per ottenere tutte le modifiche apportate a un object in Entity Framework prima di salvare tutte le modifiche. La ragione di ciò è che voglio creare una tabella di log nel nostro database dei clienti:

così…

C’è un modo per ottenere i valori attuali del database (vecchio) e i nuovi valori (corrente) prima che le modifiche vengano salvate?

In caso contrario, come posso ottenerlo in un modo generico, quindi tutti i miei modelli di visualizzazione possono ereditare da questo? (Sto usando la struttura MVVM + M)

È ansible utilizzare ObjectStateManager di ObjectContext , GetObjectStateEntry per ottenere ObjectStateEntry di un object, che mantiene i valori originali e correnti nelle proprietà OriginalValues e CurrentValues . È ansible ottenere i nomi delle proprietà modificate utilizzando il metodo GetModifiedProperties .

Puoi scrivere qualcosa come:

var myObjectState=myContext.ObjectStateManager.GetObjectStateEntry(myObject); var modifiedProperties=myObjectState.GetModifiedProperties(); foreach(var propName in modifiedProperties) { Console.WriteLine("Property {0} changed from {1} to {2}", propName, myObjectState.OriginalValues[propName], myObjectState.CurrentValues[propName]); } 

Per EF5 verso l’alto è ansible registrare le modifiche nel metodo SaveChanges () in questo modo:

  public override int SaveChanges() { var changes = from e in this.ChangeTracker.Entries() where e.State != System.Data.EntityState.Unchanged select e; foreach (var change in changes) { if (change.State == System.Data.EntityState.Added) { // Log Added } else if (change.State == System.Data.EntityState.Modified) { // Log Modified var item = change.Cast().Entity; var originalValues = this.Entry(item).OriginalValues; var currentValues = this.Entry(item).CurrentValues; foreach (string propertyName in originalValues.PropertyNames) { var original = originalValues[propertyName]; var current = currentValues[propertyName]; if (!Equals(original, current)) { // log propertyName: original --> current } } } else if (change.State == System.Data.EntityState.Deleted) { // log deleted } } // don't forget to save base.SaveChanges(); } 

Uso questa funzione di estensione che fornisce dettagli sull’entity framework da modificare, i valori vecchi e nuovi, il tipo di dati e la chiave dell’ quadro.

Questo è testato con EF6.1 utilizzando ObjectContext e utilizza log4net per l’output.

  ///  /// dump changes in the context to the debug log /// Debug logging must be turned on using log4net ///  /// The context to dump the changes for public static void DumpChanges(this ObjectContext context) { context.DetectChanges(); // Output any added entries foreach (var added in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added)) { Log.DebugFormat("{0}:{1} {2} {3}", added.State, added.Entity.GetType().FullName, added.Entity.ToString(), string.Join(",", added.CurrentValues.GetValue(1), added.CurrentValues.GetValue(2))); } foreach (var modified in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)) { // Put original field values into dictionary var originalValues = new Dictionary(); for (var i = 0; i < modified.OriginalValues.FieldCount; ++i) { originalValues.Add(modified.OriginalValues.GetName(i), i); } // Output each of the changed properties. foreach (var entry in modified.GetModifiedProperties()) { var originalIdx = originalValues[entry]; Log.DebugFormat("{6} = {0}.{4} [{7}][{2}] [{1}] --> [{3}] Rel:{5}", modified.Entity.GetType(), modified.OriginalValues.GetValue(originalIdx), modified.OriginalValues.GetFieldType(originalIdx), modified.CurrentValues.GetValue(originalIdx), modified.OriginalValues.GetName(originalIdx), modified.IsRelationship, modified.State, string.Join(",", modified.EntityKey.EntityKeyValues.Select(v => string.Join(" = ", v.Key, v.Value)))); } } // Output any deleted entries foreach (var deleted in context.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted)) { Log.DebugFormat("{1} {0} {2}", deleted.Entity.GetType().FullName, deleted.State, string.Join(",", deleted.CurrentValues.GetValue(1), deleted.CurrentValues.GetValue(2))); } } 

È ansible implementare questo in C # in una manciata di modi. Il seguente codice ne dimostra uno.

Requisiti

  • DevExpress Mvvm Framework
  • System.ValueTuple

installazioni

  • PM> Install-Package DevExpressMvvm -Version 17.1.6
  • PM> Install-Package System.ValueTuple -Version 4.4.0

Esempio

 using DevExpress.Mvvm; using DevExpress.Mvvm.Native; using System; using System.Linq; using System.ComponentModel; using System.Collections.Generic; namespace TrackingPropertyManagerApp { public class Member : BindableBase { public Member() { PropertyManager = new TrackingPropertyManager(); } private string _MemberProperty; [DisplayName("Your property description")] public string MemberProperty { get { return _MemberProperty; } set { SetProperty(ref _MemberProperty, value, () => MemberProperty); } } private TrackingPropertyManager PropertyManager { get; } protected override PropertyManager CreatePropertyManager() => PropertyManager; public IEnumerable<(string property, object original, object changed)> GetChangedProperties() => from changes in PropertyManager.ChangeTracker where changes.Value.wasChanged select (property: changes.Key, original: changes.Value.original, changed: changes.Value.changed); private class TrackingPropertyManager : PropertyManager { internal IDictionary ChangeTracker = new Dictionary(); protected override bool SetPropertyCore(ref T storage, T value, string propertyName) { if (ChangeTracker.TryGetValue(propertyName, out (object original, bool wasChanged, object changed) changeInfo)) { if (!EqualityComparer.Default.Equals(storage, value)) { ChangeTracker[propertyName] = (changeInfo.original, true, value); } } else { ChangeTracker[propertyName] = (value, false, null); } return base.SetPropertyCore(ref storage, value, propertyName); } } } class Program { static void Main(string[] args) { var member = new Member(); member.MemberProperty = "first value"; member.MemberProperty = "second value"; member.MemberProperty = "last value"; var changes = member.GetChangedProperties().ToArray(); var type = typeof(Member); foreach (var (property, original, changed) in changes) { var propertyInfo = type.GetProperty(property); if (propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault() is DisplayNameAttribute displayAttribute) { Console.WriteLine($"{ displayAttribute.DisplayName }: old='{ original }' new='{ changed }'"); } } Console.ReadLine(); } } } 

Produzione

La descrizione della tua proprietà: old = ‘first value’ new = ‘last value’