0

What's the best way to compare 2 versions of the same object and return a list of differences (name of property, old value and new value) See object graph below for an example. What if there was a change in the Product name how would I bubble that up into a list of property differences?

     static void Main(string[] args)
    {
        Customer c1 = new Customer();
        c1.DBA = "Test1";
        c1.LatestOrder.DateOrdered = new DateTime(2011, 7, 12);
        c1.LatestOrder.OrderDetails.Product = "Product1";

        Customer c2 = new Customer();
        c2.DBA = "Test1";
        c2.LatestOrder.DateOrdered = new DateTime(2011, 7, 12);
        c2.LatestOrder.OrderDetails.Product = "Product2";


    }

So the test above shows that everything in the 2 objects are the same except for the product name. Maybe, just as proof of concept, a list showing the property name, old value and new value.

   public class Customer
    {
        public string DBA { get; set; }
        public Order LatestOrder { get; set; }


        public Customer()
        {
            LatestOrder = new Order();
        }
    }

    public class Order
    {
        public int Id { get; set; }
        public DateTime DateOrdered { get; set; }

        public OrderDetails OrderDetails { get; set; }

        public Order()
        {
            OrderDetails = new OrderDetails();
        }
    }

    public class OrderDetails
    {
        public String Product { get; set; }
    }
}
Rod
  • 14,529
  • 31
  • 118
  • 230

4 Answers4

1

this may help you get started, essentially if you know obj1 and obj2 are of the same type, you get all of the public properties and compare them one by one... you may want to handle collections differently though (comparing each item in the collection)...

you could then compile all this info in some dictionary or custom object.

foreach (var info in obj1.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
    var val1 = info.GetValue(obj1, null);
    var val2 = info.GetValue(obj2, null);
    // check if val1 == val2
}
Jon Erickson
  • 112,242
  • 44
  • 136
  • 174
1

You could try it using reflection. Something like this:

class Difference
{
    private Difference(string propertyPath, object value1, object value2)
    {
        PropertyPath = propertyPath;
        Value1 = value1;
        Value2 = value2;
    }

    public string PropertyPath { get; private set; }
    public object Value1 { get; private set; }
    public object Value2 { get; private set; }

    public Difference Extend(string propertyName)
    {
        return new Difference(
            string.Format("{0}.{1}", propertyName, PropertyPath), Value1, Value2);
    }

    public override string ToString()
    {
        return string.Format("{0}: {1}, {2}", PropertyPath, Value1, Value2);
    }

    public static IEnumerable<Difference> GetDifferences<T>(T value1, T value2)
    {
        return GetDifferences(typeof(T), value1, value2);
    }

    // types in this collection are compared directly
    // and not recursively using their properties
    private static readonly Type[] PrimitiveTypes =
        new[] { typeof(int), typeof(string), typeof(DateTime) };

    public static IEnumerable<Difference> GetDifferences(
        Type type, object obj1, object obj2)
    {
        foreach (var property in
            type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            var val1 = property.GetValue(obj1, null);
            var val2 = property.GetValue(obj2, null);
            if (PrimitiveTypes.Contains(property.PropertyType))
            {
                if (!val1.Equals(val2))
                    yield return new Difference(property.Name, val1, val2);
            }
            else
            {
                foreach (var difference in
                    GetDifferences(property.PropertyType, val1, val2))
                    yield return difference.Extend(property.Name);
            }
        }
    }
}

This recursively walks the object graph and returns something like

LatestOrder.DateOrdered: 12.7.2011 0:00:00, 11.7.2011 0:00:00
LatestOrder.OrderDetails.Product: Product1, Product2

Doing this is quite fragile, though. For example, it could easily cause stack overflow if you have any kind of cyclic relations. (For example, DateTime does, in the form of the Date property, so I had to include it in primitive types.) And if you have some property that depends on other properties, one actual difference may be reported multiple times. (If DateTime wasn't cyclic, this would happen there: two DateTimes that differ in the Seconds property also differ in TotalSeconds, TotalMinutes, etc.)

svick
  • 236,525
  • 50
  • 385
  • 514
  • so are there alternatives that are less fragile? I'm not saying this won't work for me I'm just asking best practices. – Rod Jul 14 '11 at 17:42
  • @rod, you could create an interface for that. But you would have to implement it for every class you want to use this on. – svick Jul 14 '11 at 22:30
  • by the way could you help me or direct me to where i can learn about GetDifferences(... specifically what is and how it works? – Rod Jul 18 '11 at 13:07
  • @rod, it's a [generic method](http://msdn.microsoft.com/en-us/library/twcad0zb.aspx). – svick Jul 18 '11 at 18:10
0

You could do something similar to the Comparable class, but with a list of differences instead of an integer. For example

public class ClassName {
    ...
    ArrayList compareTo(ClassName other) {
        if (this.attribute.equals(other.attribute)) {
            add to list
        }
    }
}
NoBugs
  • 9,310
  • 13
  • 80
  • 146
0

If the old version and the new version are in the same object instance, maybe you can use a variant of the Memento Pattern (http://en.wikipedia.org/wiki/Memento_pattern).

If you are thinking to create a reusable component, you should think about the use of reflection classes. With reflection you can view all the properties values, even if you don't know all the properties names.

fdaines
  • 1,216
  • 10
  • 12