Reputation: 6016
Is there a way to get the original Entity itself from the ChangeTracker
(rather than just the original values)?
If the State
is Modified
, 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.
Upvotes: 11
Views: 23645
Reputation: 236228
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;
}
Upvotes: 24
Reputation: 527
While working with EF 6 i used the following code to get the underlying POCO entity type from proxy type,
var entityType = ObjectContext.GetObjectType(dbEntitymodifiedEntry.Entity.GetType());
ObjectContext.GetObjectType
: Return the POCO from proxy object
Upvotes: -1
Reputation: 4297
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));
}
Upvotes: 5
Reputation: 252
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.
Upvotes: 1