/// <summary> /// Compares the properties of two objects of the same type and returns if all properties are equal. /// </summary> /// <param name="objectA">The first object to compare.</param> /// <param name="objectB">The second object to compre.</param> /// <param name="ignoreList">A Dictionary to ignore from the comparison where keys is type and vae is list of property names </param> /// <returns><c>true</c> if all property values are equal, otherwise <c>false</c>.</returns> public static bool AreObjectsEqual(object objectA, object objectB, IgnoreList ignoreList = null) { bool result; if (objectA != null && objectB != null) { Type objectType; objectType = objectA.GetType(); result = true; // assume by default they are equal foreach (PropertyInfo propertyInfo in objectType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && !IsPresentInIgnoreList(ignoreList, objectType, p))) { object valueA; object valueB; ParameterInfo[] par = propertyInfo.GetIndexParameters(); if (par.Length > 0) { //we shouldn't compare indexed properties continue; } valueA = propertyInfo.GetValue(objectA, null); valueB = propertyInfo.GetValue(objectB, null); // if it is a primative type, value type or implements IComparable, just directly try and compare the value if (CanDirectlyCompare(propertyInfo.PropertyType)) { if (!AreValuesEqual(valueA, valueB)) { Console.WriteLine("Mismatch with property '{0}.{1}' found. Expected: {2}, was {3}", objectType.FullName, propertyInfo.Name, valueA, valueB); result = false; } } // if it implements IEnumerable, then scan any items else if (typeof(IEnumerable).IsAssignableFrom(propertyInfo.PropertyType)) { // null check if (valueA == null && valueB != null || valueA != null && valueB == null) { Console.WriteLine("Mismatch with property '{0}.{1}' found. Expected: {2}, was {3}", objectType.FullName, propertyInfo.Name, valueA, valueB); result = false; } else if (valueA != null && valueB != null) { var valueAcollection = ((IEnumerable)valueA).Cast <object>(); var valueBcollection = ((IEnumerable)valueB).Cast <object>(); // and if they do, compare each item... this assumes both collections have the same order valueAcollection.ZipLongest(valueBcollection, (first, second) => { var collectionItemType = first.GetType(); if (CanDirectlyCompare(collectionItemType)) { if (!AreValuesEqual(first, second)) { Console.WriteLine("Item {0} in property collection '{1}.{2}' does not match.", 0, objectType.FullName, propertyInfo.Name); result = false; } } else if (!AreObjectsEqual(first, second, ignoreList)) { Console.WriteLine("Item {0} in property collection '{1}.{2}' does not match.", 0, objectType.FullName, propertyInfo.Name); result = false; } return(true); }); //// check the counts to ensure they match //if (collectionItemsCount1 != collectionItemsCount2) //{ // Console.WriteLine("Collection counts for property '{0}.{1}' do not match.", objectType.FullName, propertyInfo.Name); // result = false; //} } } else if (propertyInfo.PropertyType.IsClass) { if (!AreObjectsEqual(propertyInfo.GetValue(objectA, null), propertyInfo.GetValue(objectB, null), ignoreList)) { Console.WriteLine("Mismatch with property '{0}.{1}' found. Expected: {2}, was {3}", objectType.FullName, propertyInfo.Name, valueA, valueB); result = false; } } else { Console.WriteLine("Cannot compare property '{0}.{1}'.", objectType.FullName, propertyInfo.Name); result = false; } } } else { result = object.Equals(objectA, objectB); } return(result); }