How to get original Entity from ChangeTracker

Override SaveChanges of DbContext or just access ChangeTracker from the context:

foreach (var entry in context.ChangeTracker.Entries<Foo>())
{
    if (entry.State == System.Data.EntityState.Modified)
    {
        // use entry.OriginalValues
        Foo originalFoo = CreateWithValues<Foo>(entry.OriginalValues);
    }
}

Here is a method which will create a new entity with the original values. Thus all entities should have a parameterless public constructor, you can simply construct an instance with new:

private T CreateWithValues<T>(DbPropertyValues values)
    where T : new()
{
    T entity = new T();
    Type type = typeof(T);

    foreach (var name in values.PropertyNames)
    {
        var property = type.GetProperty(name);
        property.SetValue(entity, values.GetValue<object>(name));
    }

    return entity;
}

I would suggest clone entities on materialization and attach them to second context to keep whole original objects graph (if you need it of course). You can make them all ICloneable by modifying T4 template.


Nice. Here is a slightly modified version that will handle complex properties:

public static TEntity GetOriginal<TEntity>(this DbContext ctx, TEntity updatedEntity) where TEntity : class
    {
        Func<DbPropertyValues, Type, object> getOriginal = null;
        getOriginal = (originalValues, type) =>
             {
                 object original = Activator.CreateInstance(type, true);
                 foreach (var ptyName in originalValues.PropertyNames)
                 {
                     var property = type.GetProperty(ptyName);
                     object value = originalValues[ptyName];
                     if (value is DbPropertyValues) //nested complex object
                     {
                         property.SetValue(original, getOriginal(value as DbPropertyValues, property.PropertyType));
                     }
                     else
                     {
                         property.SetValue(original, value);
                     }
                 }
                 return original;
             };
        return (TEntity)getOriginal(ctx.Entry(updatedEntity).OriginalValues, typeof(TEntity));
    }