private List <string> GetMatchingSpec(ComparisonResult result, Type type) { if (type == null) { return new List <string> { "(null)" } } ; //The user defined a key for the order var matchingBasePresent = result.Config.CollectionMatchingSpec.Keys.FirstOrDefault(k => k.IsAssignableFrom(type)); if (matchingBasePresent != null) { return(result.Config.CollectionMatchingSpec.First(p => p.Key == matchingBasePresent).Value.ToList()); } //Make a key out of primitive types, date, decimal, string, guid, and enum of the class List <string> list = Cache.GetPropertyInfo(result.Config, type) .Where(o => o.CanWrite && !ExcludeLogic.ShouldExcludeMember(result.Config, o, o.DeclaringType) && (TypeHelper.IsSimpleType(o.PropertyType) || TypeHelper.IsEnum(o.PropertyType))) .Select(o => o.Name).ToList(); return(list); } }
/// <summary> /// Compare two objects that implement IList /// </summary> public override void CompareType(CompareParms parms) { //This should never happen, null check happens one level up if (parms.Object1 == null || parms.Object2 == null) { return; } try { parms.Result.AddParent(parms.Object1); parms.Result.AddParent(parms.Object2); Type t1 = parms.Object1.GetType(); Type t2 = parms.Object2.GetType(); //Check if the class type should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2)) { return; } parms.Object1Type = t1; parms.Object2Type = t2; if (parms.Result.ExceededDifferences) { return; } bool countsDifferent = ListsHaveDifferentCounts(parms); // If items is collections, need to use default compare logic, not ignore order logic. // We cannot ignore order for nested collections because we will get an reflection exception. // May be need to display some warning or write about this behavior in documentation. if (parms.Config.IgnoreCollectionOrder && !ChildShouldBeComparedWithoutOrder(parms)) { // TODO: allow IndexerComparer to works with types (now it works only with properties). IgnoreOrderLogic ignoreOrderLogic = new IgnoreOrderLogic(RootComparer); ignoreOrderLogic.CompareEnumeratorIgnoreOrder(parms, countsDifferent); } else { CompareItems(parms); } //Properties on the root of a collection CompareProperties(parms); CompareFields(parms); } finally { parms.Result.RemoveParent(parms.Object1); parms.Result.RemoveParent(parms.Object2); } }
/// <summary> /// Compare two objects that implement IList /// </summary> public override void CompareType(CompareParms parms) { //This should never happen, null check happens one level up if (parms.Object1 == null || parms.Object2 == null) { return; } try { parms.Result.AddParent(parms.Object1.GetHashCode()); parms.Result.AddParent(parms.Object2.GetHashCode()); Type t1 = parms.Object1.GetType(); Type t2 = parms.Object2.GetType(); //Check if the class type should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2)) { return; } parms.Object1Type = t1; parms.Object2Type = t2; if (parms.Result.ExceededDifferences) { return; } bool countsDifferent = ListsHaveDifferentCounts(parms); if (parms.Config.IgnoreCollectionOrder && !ChildIsListOrDictionary(parms)) { IgnoreOrderLogic ignoreOrderLogic = new IgnoreOrderLogic(RootComparer); ignoreOrderLogic.CompareEnumeratorIgnoreOrder(parms, countsDifferent); } else { CompareItems(parms); } //Properties on the root of a collection CompareProperties(parms); CompareFields(parms); } finally { parms.Result.RemoveParent(parms.Object1.GetHashCode()); parms.Result.RemoveParent(parms.Object2.GetHashCode()); } }
private void CompareField(CompareParms parms, FieldInfo item) { //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(item.FieldType)) { return; } //Skip if it should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeMember(parms.Config, item)) { return; } //If we ignore types then we must get correct FieldInfo object FieldInfo secondFieldInfo = GetSecondFieldInfo(parms, item); //If the field does not exist, and we are ignoring the object types, skip it if (parms.Config.IgnoreObjectTypes && secondFieldInfo == null) { return; } object objectValue1 = item.GetValue(parms.Object1); object objectValue2 = secondFieldInfo != null?secondFieldInfo.GetValue(parms.Object2) : null; bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.IsParent(objectValue1)); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.IsParent(objectValue2)); //Skip fields that point to the parent if ((TypeHelper.IsClass(item.FieldType) || TypeHelper.IsInterface(item.FieldType)) && (object1IsParent || object2IsParent)) { return; } string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, item.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb }; _rootComparer.Compare(childParms); }
private static List <PropertyEntity> AddPropertyInfos(CompareParms parms, object objectValue, Type objectType, IEnumerable <PropertyInfo> properties) { List <PropertyEntity> currentProperties = new List <PropertyEntity>(); foreach (var property in properties) { if (ExcludeLogic.ShouldExcludeMember(parms.Config, property, objectType)) { continue; } PropertyEntity propertyEntity = new PropertyEntity(); propertyEntity.IsDynamic = false; propertyEntity.Name = property.Name; propertyEntity.CanRead = property.CanRead; propertyEntity.CanWrite = property.CanWrite; propertyEntity.PropertyType = property.PropertyType; #if !NETSTANDARD propertyEntity.ReflectedType = property.ReflectedType; #endif propertyEntity.Indexers.AddRange(property.GetIndexParameters()); propertyEntity.DeclaringType = objectType; if (propertyEntity.CanRead && (propertyEntity.Indexers.Count == 0)) { try { propertyEntity.Value = property.GetValue(objectValue, null); } catch (System.Reflection.TargetInvocationException) { } catch (System.NotSupportedException) { } } propertyEntity.PropertyInfo = property; currentProperties.Add(propertyEntity); } return(currentProperties); }
/// <summary> /// Compare two classes /// </summary> public override void CompareType(CompareParms parms) { try { parms.Result.AddParent(parms.Object1); parms.Result.AddParent(parms.Object2); //Custom classes that implement IEnumerable may have the same hash code //Ignore objects with the same hash code if (!(parms.Object1 is IEnumerable) && ReferenceEquals(parms.Object1, parms.Object2)) { return; } Type t1 = parms.Object1.GetType(); Type t2 = parms.Object2.GetType(); //Check if the class type should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2)) { return; } parms.Object1Type = t1; parms.Object2Type = t2; //Compare the properties if (parms.Config.CompareProperties) { _propertyComparer.PerformCompareProperties(parms); } //Compare the fields if (parms.Config.CompareFields) { _fieldComparer.PerformCompareFields(parms); } } finally { parms.Result.RemoveParent(parms.Object1); parms.Result.RemoveParent(parms.Object2); } }
public override void CompareType(CompareParms parms) { Type t1 = parms.Object1.GetType(); Type t2 = parms.Object2.GetType(); //Check if the class type should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2)) { return; } parms.Object1Type = t1; parms.Object2Type = t2; if (!parms.Config.IgnoreCollectionOrder) { CompareItems(parms); } }
/// <summary> /// Compare two collections. /// </summary> public override void CompareType(CompareParms parms) { try { parms.Result.AddParent(parms.Object1); parms.Result.AddParent(parms.Object2); Type t1 = parms.Object1.GetType(); Type t2 = parms.Object2.GetType(); //Check if the class type should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2)) { return; } parms.Object1Type = t1; parms.Object2Type = t2; bool countsDifferent = CollectionsDifferentCount(parms); if (parms.Result.ExceededDifferences) { return; } if (parms.Config.IgnoreCollectionOrder) { IgnoreOrderLogic logic = new IgnoreOrderLogic(RootComparer); logic.CompareEnumeratorIgnoreOrder(parms, countsDifferent); } else { CompareItems(parms); } } finally { parms.Result.RemoveParent(parms.Object1); parms.Result.RemoveParent(parms.Object2); } }
/// <summary> /// Compare a single property of a class /// </summary> /// <param name="parms"></param> /// <param name="info"></param> /// <param name="object2Properties"></param> private void CompareProperty(CompareParms parms, PropertyEntity info, List <PropertyEntity> object2Properties) { //If we can't read it, skip it if (info.CanRead == false) { return; } //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(info.PropertyType)) { return; } //Skip if it should be excluded based on the configuration if (info.PropertyInfo != null && ExcludeLogic.ShouldExcludeMember(parms.Config, info.PropertyInfo)) { return; } //This is a dynamic property to be excluded on an expando object if (info.IsDynamic && ExcludeLogic.ShouldExcludeDynamicMember(parms.Config, info.Name, info.DeclaringType)) { return; } //If we should ignore read only, skip it if (!parms.Config.CompareReadOnly && info.CanWrite == false) { return; } //If we ignore types then we must get correct PropertyInfo object PropertyEntity secondObjectInfo = GetSecondObjectInfo(info, object2Properties); //If the property does not exist, and we are ignoring the object types, skip it if (parms.Config.IgnoreObjectTypes && secondObjectInfo == null) { return; } object objectValue1; object objectValue2; if (!IsValidIndexer(parms.Config, info, parms.BreadCrumb)) { objectValue1 = info.Value; objectValue2 = secondObjectInfo != null ? secondObjectInfo.Value : null; } else { _indexerComparer.CompareIndexer(parms, info, secondObjectInfo); return; } bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.Parents.ContainsKey(objectValue1.GetHashCode())); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.Parents.ContainsKey(objectValue2.GetHashCode())); //Skip properties where both point to the corresponding parent if ((TypeHelper.IsClass(info.PropertyType) || TypeHelper.IsInterface(info.PropertyType) || TypeHelper.IsStruct(info.PropertyType)) && (object1IsParent && object2IsParent)) { return; } string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, info.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb }; _rootComparer.Compare(childParms); }
/// <summary> /// Compare the properties of a class /// </summary> public void PerformCompareProperties(CompareParms parms) { IEnumerable <PropertyInfo> currentProperties = null; //Interface Member Logic if (parms.Config.InterfaceMembers.Count > 0) { Type[] interfaces = parms.Object1Type.GetInterfaces(); foreach (var type in parms.Config.InterfaceMembers) { if (interfaces.Contains(type)) { currentProperties = Cache.GetPropertyInfo(parms.Result, type); break; } } } if (currentProperties == null) { currentProperties = Cache.GetPropertyInfo(parms.Result, parms.Object1Type); } foreach (PropertyInfo info in currentProperties) { //If we can't read it, skip it if (info.CanRead == false) { continue; } //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(info.PropertyType)) { continue; } //Skip if it should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeMember(parms.Config, info)) { continue; } //If we should ignore read only, skip it if (!parms.Config.CompareReadOnly && info.CanWrite == false) { continue; } //If we ignore types then we must get correct PropertyInfo object PropertyInfo secondObjectInfo = null; if (parms.Config.IgnoreObjectTypes) { var secondObjectPropertyInfos = Cache.GetPropertyInfo(parms.Result, parms.Object2Type); foreach (var propertyInfo in secondObjectPropertyInfos) { if (propertyInfo.Name != info.Name) { continue; } secondObjectInfo = propertyInfo; break; } } else { secondObjectInfo = info; } object objectValue1; object objectValue2; if (!IsValidIndexer(parms.Config, info, parms.BreadCrumb)) { objectValue1 = info.GetValue(parms.Object1, null); objectValue2 = secondObjectInfo != null?secondObjectInfo.GetValue(parms.Object2, null) : null; } else { _indexerComparer.CompareIndexer(parms, info); continue; } bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.Parents.ContainsKey(objectValue1.GetHashCode())); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.Parents.ContainsKey(objectValue2.GetHashCode())); //Skip properties where both point to the corresponding parent if ((TypeHelper.IsClass(info.PropertyType) || TypeHelper.IsInterface(info.PropertyType) || TypeHelper.IsStruct(info.PropertyType)) && (object1IsParent && object2IsParent)) { continue; } string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, info.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb }; _rootComparer.Compare(childParms); if (parms.Result.ExceededDifferences) { return; } } }
/// <summary> /// Compare a single property of a class /// </summary> /// <param name="parms"></param> /// <param name="info"></param> private void CompareProperty(CompareParms parms, PropertyInfo info) { //If we can't read it, skip it if (info.CanRead == false) { return; } //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(info.PropertyType)) { return; } //Skip if it should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeMember(parms.Config, info)) { return; } //If we should ignore read only, skip it if (!parms.Config.CompareReadOnly && info.CanWrite == false) { return; } //If we ignore types then we must get correct PropertyInfo object PropertyInfo secondObjectInfo = GetSecondObjectInfo(parms, info); //If the property does not exist, and we are ignoring the object types, skip it if (parms.Config.IgnoreObjectTypes && secondObjectInfo == null) { return; } object objectValue1; object objectValue2; if (!IsValidIndexer(parms.Config, info, parms.BreadCrumb)) { objectValue1 = info.GetValue(parms.Object1, null); objectValue2 = secondObjectInfo != null?secondObjectInfo.GetValue(parms.Object2, null) : null; } else { _indexerComparer.CompareIndexer(parms, info); return; } bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.Parents.ContainsKey(objectValue1.GetHashCode())); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.Parents.ContainsKey(objectValue2.GetHashCode())); //Skip properties where both point to the corresponding parent if ((TypeHelper.IsClass(info.PropertyType) || TypeHelper.IsInterface(info.PropertyType) || TypeHelper.IsStruct(info.PropertyType)) && (object1IsParent && object2IsParent)) { return; } /* var name = info.Name; * var resource = string.Empty; * foreach (DisplayAttribute item in info.GetCustomAttributes(typeof(DisplayAttribute), true)) * { * if (item.ResourceType != null) * { * resource = Convert.ToString(item.ResourceType.GetProperty(item.Name, BindingFlags.Static | BindingFlags.Public).GetValue(null, null)); * } * if (string.IsNullOrWhiteSpace(resource)) * { * if (!string.IsNullOrWhiteSpace(item.Description)) * { * name = item.Description; * } * else if (!string.IsNullOrWhiteSpace(item.Prompt)) * { * name = item.Prompt; * } * else if (!string.IsNullOrWhiteSpace(item.Name)) * { * name = item.Name; * } * } * else * { * name = resource; * } * } */ string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, info.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb }; _rootComparer.Compare(childParms); }
/// <summary> /// Compare the fields of a class /// </summary> public void PerformCompareFields(CompareParms parms) { IEnumerable <FieldInfo> currentFields = null; //Interface Member Logic if (parms.Config.InterfaceMembers.Count > 0) { Type[] interfaces = parms.Object1Type.GetInterfaces(); foreach (var type in parms.Config.InterfaceMembers) { if (interfaces.Contains(type)) { currentFields = Cache.GetFieldInfo(parms.Config, type); break; } } } if (currentFields == null) { currentFields = Cache.GetFieldInfo(parms.Config, parms.Object1Type); } foreach (FieldInfo item in currentFields) { //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(item.FieldType)) { continue; } //Skip if it should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeMember(parms.Config, item)) { continue; } object objectValue1 = item.GetValue(parms.Object1); object objectValue2 = item.GetValue(parms.Object2); bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.Parents.ContainsKey(objectValue1.GetHashCode())); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.Parents.ContainsKey(objectValue2.GetHashCode())); //Skip fields that point to the parent if ((TypeHelper.IsClass(item.FieldType) || TypeHelper.IsInterface(item.FieldType)) && (object1IsParent || object2IsParent)) { continue; } string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, item.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb }; _rootComparer.Compare(childParms); if (parms.Result.ExceededDifferences) { return; } } }
/// <summary> /// Compare a single property of a class /// </summary> /// <param name="parms"></param> /// <param name="info"></param> /// <param name="object2Properties"></param> private void CompareProperty(CompareParms parms, PropertyEntity object1Property, PropertyEntity object2Property) { //If we can't read it, skip it if (object1Property?.CanRead == false || object2Property.CanRead == false) { return; } //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(object1Property?.PropertyType) || !parms.Config.CompareChildren && TypeHelper.CanHaveChildren(object2Property.PropertyType)) { return; } //Skip if it should be excluded based on the configuration if (object1Property?.PropertyInfo != null && ExcludeLogic.ShouldExcludeMember(parms.Config, object1Property.PropertyInfo) || object2Property.PropertyInfo != null && ExcludeLogic.ShouldExcludeMember(parms.Config, object2Property.PropertyInfo)) { return; } //If we should ignore read only, skip it if (!parms.Config.CompareReadOnly && object1Property?.CanWrite == false || !parms.Config.CompareReadOnly && object2Property.CanWrite == false) { return; } //If the property does not exist, and we are ignoring the object types, skip it //We need to disscus this!!!! //if (parms.Config.IgnoreObjectTypes && secondObjectInfo == null) // return; object objectValue1 = object1Property?.Value; object objectValue2 = object2Property?.Value; /* * //need deep investigation about indexerComparer!!! * if (!IsValidIndexer(parms.Config, object1Property, parms.BreadCrumb)) * { * objectValue1 = object1Property.Value; * objectValue2 = object2Property != null ? object2Property.Value : null; * } * else * { * _indexerComparer.CompareIndexer(parms, object1Property); * return; * } * //need deep investigation about indexerComparer!!! */ bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Object1.GetHashCode().Equals(objectValue1.GetHashCode())); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Object2.GetHashCode().Equals(objectValue2.GetHashCode())); //Skip properties where both point to the corresponding parent if ((TypeHelper.IsClass(object1Property?.PropertyType) || TypeHelper.IsInterface(object1Property?.PropertyType) || TypeHelper.IsStruct(object1Property?.PropertyType) || TypeHelper.IsClass(object2Property.PropertyType) || TypeHelper.IsInterface(object2Property.PropertyType) || TypeHelper.IsStruct(object2Property.PropertyType)) && (object1IsParent && object2IsParent)) { return; } string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, object1Property.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb }; _rootComparer.Compare(childParms); }