How to get original Entity from ChangeTracker
Solution 1
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;
}
Solution 2
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));
}
Solution 3
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.
Eric
Updated on September 14, 2020Comments
-
Eric over 3 years
Is there a way to get the original Entity itself from the
ChangeTracker
(rather than just the original values)?If the
State
isModified
, then I suppose I could do this:// Get the DbEntityEntry from the DbContext.ChangeTracker... // Store the current values var currentValues = entry.CurrentValues.Clone(); // Set to the original values entry.CurrentValues.SetValues(entry.OriginalValues.Clone()); // Now we have the original entity Foo entity = (Foo)entry.Entity; // Do something with it... // Restore the current values entry.CurrentValues.SetValues(currentValues);
But this doesn't seem very nice, and I'm sure there are problems with it that I don't know about... Is there a better way?
I'm using Entity Framework 6.
-
Eric about 11 yearsI may be missing a subtlety, but I believe I know how to do this (this is just getting the original values, right?) I want an actual strongly typed representation of the original entity - not just the original values.
-
Sergey Berezovskiy about 11 years@Eric you want to have entity object with original properties values?
-
Eric about 11 yearsYes, that's correct. Perhaps a way to construct an entity given a set of original values...
-
Sergey Berezovskiy about 11 years@Eric done, I've wrote method which creates entity and set property values via reflection
-
Brikesh Kumar over 10 yearsThanks @Clement. I had this issue and spent lot of time to get the old ones..First tried to return totally disconnected objects. BUt it had its own set of issue while updating. This one works, need to test some more scenarios though
-
Brikesh Kumar over 10 yearsHey @Clement. This one doesn't hydrate the navigational properties. Any clue?
-
Clement over 10 yearsNot sure, I haven't tested with nav properties. Because it's using Activator.CreateInstance, I don't expect lazy loading of navigational properties to work (since it relies on a runtime proxy type generated by EF). You might be able to change this code to handle them...
-
Brikesh Kumar over 10 yearsThanks @Clement. I load the the graph one at a time, didn't modify to generically populate the navigation properties though.
-
Mario Duarte over 8 yearsThis only returns a type. I couldn't get the original values.