I'm writing a windows store app that receives an object (custom type that I created) from a server, lets the user edit the object and then submits a "changeset" back to the server. This changeset is simply an object of the same type as the received object with every field set to null except the fields the user edited. Those fields then contain the user's edits like so:
Original Case: Description: "Cracked exhaust pipe" Status: "Open" Customer: "" Mileage: 10000 Edited Case: Description: "" Status "Open" Customer: "Example inc." Mileage: 10000 Case changeset: Description: "" Status: null Customer: "Example inc." Mileage: null
When the app first downloads the object from the server I make a copy of it for later comparison. The user then makes changes to one of the objects and when the user submits his changes, a changeset of these two objects is calculated with a generic method CalculateChangeSet
. It goes through every property of the two objects and compares them for equality:
public static T CalculateChangeSet<T>(T oldObject, T newObject) where T : new()
{
T changeSet = (T)Activator.CreateInstance(oldObject.GetType());
foreach (PropertyInfo property in oldObject.GetType().GetRuntimeProperties())
{
var oldValue = property.GetValue(oldObject);
var newValue = newObject.GetType().GetRuntimeProperty(property.Name).GetValue(newObject);
if (oldValue != null && newValue != null)
{
if (oldValue is IList)
{
Type listType = oldValue.GetType().GetRuntimeProperty("Item").PropertyType;
IList<listType> oldList = (IList<listType>)oldValue; //Visual studio complains about
IList<listType> newList = (IList<listType>)newValue; //listType not being found
if (!oldList.SequenceEqual(newList))
{
changeSet.GetType().GetRuntimeProperty(property.Name).SetValue(changeSet, newValue, null);
}
}
else
{
if (!oldValue.Equals(newValue))
{
changeSet.GetType().GetRuntimeProperty(property.Name).SetValue(changeSet, CalculateChangeSet(oldValue, newValue));
}
}
}
else
{
changeSet.GetType().GetRuntimeProperty(property.Name).SetValue(changeSet, newValue);
}
}
return changeSet;
}
The method works fine for every property I've come across except for Lists so I created the if clause to deal with lists. Since list1.Equals(list2)
does not compare items in the lists, we need to cast oldValue
and newValue
to List<T>
to be able to use list.SequenceEqual()
.
Why do I get an error saying The type or namespace name 'listType' could not be found (are you missing a using directive or an assembly reference?)
when I try to use it to create new lists? If there is a better way to approach this problem I'm open to suggestions..
Try this:
IList oldList = (IList)oldValue; //Visual studio complains about
IList newList = (IList)newValue; //listType not being found
if (!NonGenericSequenceEqual(oldList, newList))
{
changeSet.GetType().GetRuntimeProperty(property.Name).SetValue(changeSet, newValue, null);
}
public static bool NonGenericSequenceEqual(IList a, IList b)
{
if (ReferenceEquals(a, b))
{
return true;
}
else if (a == null || b == null)
{
return false;
}
else
{
int count = a.Count;
if (count != b.Count)
{
return false;
}
else
{
for (int i = 0; i < count; i++)
{
if (!Object.Equals(a[i], b[i]))
{
return false;
}
}
return true;
}
}
}
Weak code is 'Object.Equals(a[i], b[i])'
Another variant via reflection:
if (!(bool)typeof(System.Linq.Enumerable).GetMethod("SequenceEqual").MakeGenericMethod(oldValue.GetType().GetRuntimeProperty("Item").PropertyType).Invoke(null, new object[] { oldObject, newObject }))
{
changeSet.GetType().GetRuntimeProperty(property.Name).SetValue(changeSet, newValue, null);
}