Welcome to MSDN Blogs Sign in | Join | Help

system.data.objects dev guy

Ramblings about ADO.Net, the Entity Framework, and other random things from a dev guy.
Creating an original value object from the separate values stored in the ObjectStateManager

Update December 3, 2007: While thinking about further uses for this routine, I decided to make a few tweaks to its semantics from what I originally published here.  First off, it seemed useful for unchanged entities to return a copy of the object rather than just directly returning the source, because I ultimately want to build an original values graph (and I don't want the current values and the original values graphs to be connected to each other).  Secondly, I wanted to set the EntityKey on the original objects if they implement IEntityWithKey.  The code below has been updated to reflect these changes.

While thinking about remoting object graphs and the like, I decided to build up a few useful routines to facilitate my experiments.   And of course once I put something like this together, I figure maybe someone else would be interested…

The first of these is a method which will construct an object  corresponding to the original values of an attached entity.  When an entity is attached, the ObjectStateManager will track all changes made to the object, but it optimizes the memory used by maintaining the original values of only those properties which have been changed, so if you want a whole object which has the properties of the original, you need to construct it yourself.

The ObjectStateEntry has a few important features which make this process easier: 

·         It exposes a DbDataRecord of both the current and original values that gives you a nice, late-bound interface to the data.

·         The DbDataRecord contains only those properties which are defined in the conceptual model which is an advantage for our purposes since it will skip properties and fields that reflection would show but aren’t needed for persistence.

·         The current values for the state entry are stored in the object itself, and the current values record is just a façade over the object with dynamic methods which make reads and writes for those properties much faster than reflection (think orders of magnitude faster).

So, without further ado, here’s the routine.  Just for fun, I made it an extension method on the object context.  The

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;

}

 

Code for using it would look something like this:

using (DPMudDB db = DPMudDB.Create())

{

    Room room = db.Rooms.First<Room>();

    Console.WriteLine(room.Name);

    room.Lighting += 10;

 

    Room originalRoom = (Room) db.CreateOriginalValuesObject(room);

    Console.WriteLine("Current Lighting: {0} - Original Lighting: {1}",

                      room.Lighting, originalRoom.Lighting);

}

Next on the agenda:  Creating not just an original values object but an entire graph.  That gets a bit more complicated…  We’ll leave it for another day.

- Danny

Posted: Wednesday, November 21, 2007 6:35 PM by dsimmons@microsoft.com

Comments

system.data.objects dev guy said:

Recently I shared this post which has some code that demonstrates how to create an object matching the

# December 2, 2007 11:11 PM

system.data.objects dev guy said:

In a previous post I shared some code which can be used to compute an original value version of an object

# December 8, 2007 4:07 PM

Noticias externas said:

In a previous post I shared some code which can be used to compute an original value version of an object

# December 8, 2007 5:01 PM
Anonymous comments are disabled
Page view tracker