views:

38

answers:

3

I am in the hypothetical phase of thinking of how to do something in code, so I have no concrete code examples yet. But, let's assume you have an entity type Person (with attributes such as first name, last name, DOB, etc.). When one goes to edit a person entity, you may only update one or two fields (possibly all, but usually not). Think of this as implementing an audit trail (albeit not a legal one, just informational), i.e. Tommy changed last name from Jones to Smith on 8/28/2010.

The question is, does LINQ provide an interface to compare what was changed on the person entity from the old, existing one to the newly submitted one?

Or must one loop through the old entity and the new entities properties and do a comparison on each one manually?

Pseudo:

For each p as property in person
   If oldEntity.p <> newEntity.p then
       auditEntry = new auditEntry(oldEntity.p, newEntity.p)
   end If
Next

Or, is there another/better way (implement an IComparable interface for each entity in a partial class) that can do this?

+1  A: 

This isn't exactly what your looking for but I posted a sample of a solution I used for an application I created a few years back (pre LINQ days) on this thread:

hint for audit log approach

It might be of help and get you thinking about the solution in a slightly different way.

Kelsey
I think something like that would work great. The only part I am not 100% clear on right now is getting my fields to "implement a generic template"
Tommy
A: 

DataContext.GetChangeSet and Table.GetOriginalEntityState is what you're looking for -

http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.getchangeset.aspx

http://msdn.microsoft.com/en-us/library/bb548700.aspx

Frank Tzanabetis
Those functions tell you which objects have changed and what the original values of those objects were, but they don't tell you what properties of each object changed.
Gabe
A: 

Given the following routines:

    public static void CompareProperties<T>(object source, object target)
    {
        Type type = typeof(T);

        CompareProperties(type, source, target);

        if (type.IsInterface)
        {
            Type[] baseTypes = type.GetInterfaces();
            foreach (var baseType in baseTypes)
            {
                CompareProperties(baseType, source, target);
            }
        }
        else
        {
            type = type.BaseType;
            while (type != null)
            {
                CompareProperties(type, source, target);
                type = type.BaseType;
            }
        }
    }

    private static void CompareProperties(Type type, object source, object target)
    {
        PropertyInfo[] props = type.GetProperties(
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        PropertyInfo[] props1 = source.GetType().GetProperties(
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        PropertyInfo[] props2 = target.GetType().GetProperties(
                        BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        foreach (PropertyInfo pi in props)
        {
            if (!pi.CanRead || !pi.CanWrite)
                continue;
            var name = pi.Name;
            var pi1 = props1.Where(x => x.Name == name).FirstOrDefault();
            var pi2 = props2.Where(x => x.Name == name).FirstOrDefault();
            if(pi1 == null || pi2 == null)
                continue;

            var expected = pi1.GetValue(source, null);
            var actual = pi2.GetValue(target, null);

            if(pi.PropertyType == typeof(int?))
            {
                expected = expected ?? 0;
                actual = actual ?? 0;
            }
            if (pi2.PropertyType.IsEnum && !pi.PropertyType.IsEnum)
            {
                continue;
                expected = (int)expected;
                actual = (int)actual;
            }
            Assert.AreEqual(expected, actual, "The property '" + pi.Name + "' was not saved for the " + source.GetType().Name);

        }
    }

You can compare two objects as follows:

CompareProperties<CustomerProduct>(product1, productFromDb)
Tawani