public static object CreateOriginalValuesObject(this ObjectContext context, object source)
{
// Get the state entry of the source object
// NOTE: For now we require the object to implement IEntityWithKey.
// This is something we should be able to relax later.
Debug.Assert(source is IEntityWithKey);
EntityKey sourceKey = ((IEntityWithKey)source).EntityKey;
// This method will throw if the key is null or an entry isn't found to match it. We
// could throw nicer exceptions, but this will catch the important invalid cases.
ObjectStateEntry sourceStateEntry = context.ObjectStateManager.GetObjectStateEntry(sourceKey);
// Return null for added entities & throw an exception for detached ones. In other cases we can
// always make a new object with the original values.
switch (sourceStateEntry.State)
{
case EntityState.Added:
return null;
case EntityState.Detached:
throw new InvalidOperationException("Can’t get original values when detached.");
}
// Create target object and add it to the context so that we can easily set properties using
// the StateEntry. Since objects in the added state use temp keys, we know this won't
// conflict with anything already in the context.
object target = Activator.CreateInstance(source.GetType());
string fullEntitySetName = sourceKey.EntityContainerName + "." + sourceKey.EntitySetName;
context.AddObject(fullEntitySetName, target);
EntityKey targetKey = context.GetEntityKey(fullEntitySetName, target);
ObjectStateEntry targetStateEntry = context.ObjectStateManager.GetObjectStateEntry(targetKey);
// Copy original values from the sourceStateEntry to the targetStateEntry. This will
// cause the corresponding properties on the object to be set.
for (int i = 0; i < sourceStateEntry.OriginalValues.FieldCount; i++)
{
// TODO: For best perf we should have a switch on the type here so that we could call
// the type-specific methods and avoid boxing.
targetStateEntry.CurrentValues.SetValue(i, sourceStateEntry.OriginalValues[i]);
}
// Detach the object we just created since we only attached it temporarily in order to use
// the stateEntry.
context.Detach(target);
// Set the EntityKey property on the object (if it implements IEntityWithKey).
IEntityWithKey targetWithKey = target as IEntityWithKey;
if (targetWithKey != null)
{
targetWithKey.EntityKey = sourceKey;
}
return target;
}