Tuesday, February 14, 2012

How to get changed properties for an entity in Entity Framework

Sometimes it can be useful to find exactly which entities that have been changed before saving to the database. The most basic for check for changes it to look at the EntityState property, but this does not indicate what properties that have been changed.

To get these changes we have to use the ObjectStateManager on ObjectContext. The ObjectStateManager handles the state of all entities, and can supplied a lot of other different state information about the entities.

First we always need to get the ObjectStateEntry for the entity, this a done by calling ObjectStateManager.GetObjectStateEntry.

TestEntities entities = new TestEntities();

// Query for entity and make some changes

// To get the object state entry
var stateEntry = entities.ObjectStateManager.GetObjectStateEntry(changedEntity);

To check which properties that have changed we just do

IEnumerable modifiedProperties = stateEntry.GetModifiedProperties();

We can also get all the original and current values for the entity using

CurrentValueRecord currentValues = stateEntry.CurrentValues;
DbDataRecord originalValues = stateEntry.OriginalValues;

These two objects can are accessed by column ordinal, which you may or may not know before, but luckily there is a method to get the ordinal from a column name. To get the current and/or original value for an entity, do the following

var currentValue = currentValues.GetValue(currentValues.GetOrdinal(propertyName));
var originalValue = originalValues.GetValue(originalValues.GetOrdinal(propertyName));

Not all properties are correctly marked as changed

When using this method to generate a custom audit trail, I discovered a weird behavior in Entity Framework. Everything but the primary key, are marked as modified even though it only was "changed" to the original value. And this does not work in an audit trail.

There are two ways to solve this either manual check if a property actually has been changed, or change to generate classes for the entities. The last solution are described in my previous post, Entity Framework should only mark really updated properties as modified

The other solution is to check if the current value equals the original value using the methods described above. An example could be

var stateEntry = objectStateManager.GetObjectStateEntry(entity);
var currentValues = stateEntry.CurrentValues;
var originalValues = stateEntry.OriginalValues;
var modifiedProperties = stateEntry.GetModifiedProperties();
foreach (string modifiedProperty in modifiedProperties)
{
  var currentValue = currentValues.GetValue(currentValues.GetOrdinal(modifiedProperty));
  var originalValue = originalValues.GetValue(originalValues.GetOrdinal(modifiedProperty));
  if (originalValue.Equals(currentValue))
  {
    // Value not changed
  }
  else
  {
    // Value changed
  }
}

1 comment:

  1. Thanks, this made it very easy to implement and prevent the annoying save when nothing changed.

    We replaced line 9 with following since some DB values are Char and we trim before saving.

    if (originalValue.Equals(currentValue) || (originalValue.GetType() == typeof(string) && ((string)originalValue).Trim().Equals(((string)currentValue).Trim())))

    Thanks!

    ReplyDelete