Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting all changes made to an object in the Entity Framework

People also ask

How does Entity Framework detect changes?

Detect Changes works by detecting the differences between the current property values of the entity and the original property values that are stored in a snapshot when the entity was queried or attached. The techniques shown in this topic apply equally to models created with Code First and the EF Designer.

How do I use ChangeTracker in EF core?

See Change Detection and Notifications for more information on how EF Core automatically detects changes like this. Call ChangeTracker. HasChanges() to determine whether any changes have been made that will cause SaveChanges to make updates to the database. If HasChanges return false, then SaveChanges will be a no-op.

How do you update an object in Entity Framework?

Update Objects in Entity Framework 4.0 The steps to update an existing entity are quite simple. First retrieve an instance of the entity from the EntitySet<T> (in our case ObjectSet<Customer>), then edit the properties of the Entity and finally call SaveChanges() on the context.

What is SaveChanges Entity Framework?

In Entity Framework, the SaveChanges() method internally creates a transaction and wraps all INSERT, UPDATE and DELETE operations under it. Multiple SaveChanges() calls, create separate transactions, perform CRUD operations and then commit each transaction.


You can use ObjectContext's ObjectStateManager,GetObjectStateEntry to get an object's ObjectStateEntry, which holds its original and current values in the OriginalValues and CurrentValues properties. You can get the names of the properties that changed using the GetModifiedProperties method.

You can write something like:

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]);
}

For EF5 upwards you can log your changes in the SaveChanges() method like this:

    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<IEntity>().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();
    }

I use this extension function that provides details on the entity being changed, the old and new values, the datatype, and the entity key.

This is tested with EF6.1 using ObjectContext and uses log4net for output.

    /// <summary>
    /// dump changes in the context to the debug log
    /// <para>Debug logging must be turned on using log4net</para>
    /// </summary>
    /// <param name="context">The context to dump the changes for</param>
    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<string,int>();
            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)));
        }
    }

Use IsModified field of each property accessible by Context.Entry(Entity).Properties.

In this example the modified entries are listed as a Tuple of the original an current value, indexed by name. Use any conversion that is required to build the audit log.

    using Microsoft.EntityFrameworkCore;
    using System.Collections.Generic;
    //...
    DbContext Context;  //gets somewhere in the scope
    object Entity; // Some entity that has been modified, but not saved and is being tracked by Context
    //...
    Dictionary<string, System.Tuple<object, object>> modified = 
      Context.Entry(Entity)
        .Properties.Where(p => p.IsModified)
        .ToDictionary(p => p.Metadata.Name, p => new System.Tuple<object,object>(p.OriginalValue, p.CurrentValue));
    //...

Uses Entity Framework Core 3.1. Try it for EF 6.4, but may not work.