Compare and contrast two things of the same kind.

asp.net-mvc entity-framework linq-to-sql

Question

I'm working on an mvc3 web app. When the user updates something, I want to compare the old data to the new one the user is inputing and for each field that is different add those to a log to create an activity log.

Right now this is what my save action looks like:

[HttpPost]
public RedirectToRouteResult SaveSingleEdit(CompLang newcomplang)
{
    var oldCompLang = _db.CompLangs.First(x => x.Id == newcomplang.Id);

    _db.CompLangs.Attach(oldCompLang);
    newcomplang.LastUpdate = DateTime.Today;
    _db.CompLangs.ApplyCurrentValues(newcomplang);
    _db.SaveChanges();

    var comp = _db.CompLangs.First(x => x.Id == newcomplang.Id);

    return RedirectToAction("ViewSingleEdit", comp);
}

I found that I could use this to iterate through my property of oldCompLang:

var oldpropertyInfos = oldCompLang.GetType().GetProperties();

But this doesn't really help as it only shows me the properties (Id, Name, Status...) and not the values of these properties (1, Hello, Ready...).

I could just go the hard way:

if (oldCompLang.Status != newcomplang.Status)
{
    // Add to my activity log table something for this scenario
}

But I really don't want to be doing that for all the properties of the object.

I'm not sure what's the best way to iterate through both objects to find mismatches (for example the user changed the name, or the status...) and build a list from those differences that I can store in another table.

1
15
4/27/2011 10:37:43 PM

Accepted Answer

It's not that bad, you can compare the properties "by hand" using reflection and write an extension methods for reuse - you can take this as a starting point:

public static class MyExtensions
{
    public static IEnumerable<string> EnumeratePropertyDifferences<T>(this T obj1, T obj2)
    {
        PropertyInfo[] properties = typeof(T).GetProperties();
        List<string> changes = new List<string>();

        foreach (PropertyInfo pi in properties)
        {
            object value1 = typeof(T).GetProperty(pi.Name).GetValue(obj1, null);
            object value2 = typeof(T).GetProperty(pi.Name).GetValue(obj2, null);

            if (value1 != value2 && (value1 == null || !value1.Equals(value2)))
            {
                changes.Add(string.Format("Property {0} changed from {1} to {2}", pi.Name, value1, value2));
            }
        }
        return changes;
    }
}
25
4/27/2011 11:57:33 PM

Popular Answer

If you are using EntityFramework you can get changes directly from the ObjectContext

Getting the state change entries:

  var modifiedEntries= this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);

Getting saved property names and original and changed values:

  for (int i = 0; i < stateChangeEntry.CurrentValues.FieldCount - 1; i++)
            {
                var fieldName = stateChangeEntry.OriginalValues.GetName(i);

                if (fieldName != changedPropertyName)
                    continue;

                var originalValue = stateChangeEntry.OriginalValues.GetValue(i).ToString();
                var changedValue = stateChangeEntry.CurrentValues.GetValue(i).ToString();
            }

This is better than @BrokenGlass's answer because this will go deep in the object graph for any changed states and will give you the changed properties of associated collections. It is also better because this reflects everything the ObjectContext will eventually save to the database. With the accepted solution you may get property changes that won't actually be persisted in the situation where you become disconnected via the object context.

With EF 4.0 you can also override the SaveChanges() method and wrap any auditing or activity in the same transaction as the eventual entity save meaning an audit trail won't exist without the entities being changed and vice versa. This guarantees an accurate log. If you can't guarantee an audit is accurate than its almost useless.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow