/// <summary>
 /// Creates a new instance of this class.
 /// </summary>
 /// <param name="result">The comparison's computed result.</param>
 /// <param name="property1">Property information for the property in the first object.</param>
 /// <param name="value1">Value found in the property of the first object.</param>
 /// <param name="property2">Property information for the property in the second object.</param>
 /// <param name="value2">Value found in the property of the second object.</param>
 /// <param name="mappingUsed">Mapping information used during the comparison operation.</param>
 /// <param name="exception">Exception raised during the comparison operation, if any.</param>
 internal PropertyComparisonResult(ComparisonResult result, PropertyComparisonInfo property1, object value1,
                                   PropertyComparisonInfo property2 = null, object value2 = null, PropertyMap mappingUsed = null,
                                   System.Exception exception       = null)
 {
     Result    = result;
     Property1 = property1;
     Property2 = property2;
     Value1    = value1;
     Value2    = value2;
     MapUsed   = mappingUsed;
     Exception = exception;
 }
Пример #2
0
        /// <summary>
        /// Configures the property comparison information of the source property to ignore the
        /// specified property during the comparison routine against objects of type
        /// <see cref="TDestination"/>.
        /// </summary>
        /// <typeparam name="TSourceProperty">The type of the source property.</typeparam>
        /// <param name="sourcePropExpr">Source property lambda expression.</param>
        /// <returns>This configuration object to enable fluent syntax.</returns>
        public ComparerConfiguration <TSource, TDestination> IgnoreProperty <TSourceProperty>(
            Expression <Func <TSource, TSourceProperty> > sourcePropExpr
            )
        {
            PropertyInfo           piSource = ExpressionHelper.GetPropertyInfo(sourcePropExpr, nameof(sourcePropExpr));
            PropertyComparisonInfo pci      = TypeInfo1.Properties[piSource.Name];
            //Ignore only for the specified data type.
            PropertyMap map = new PropertyMap(
                Type2,
                PropertyMapOperation.IgnoreProperty
                );

            pci.Maps.Replace(map);
            return(this);
        }
Пример #3
0
        /// <summary>
        /// Creates a copy of this object.
        /// </summary>
        /// <param name="clonePropertyMaps">A Boolean value that indicates if property maps should
        /// be cloned as well.</param>
        /// <returns>A type information object that contains the same information as this object.</returns>
        internal TypeInfo Clone(bool clonePropertyMaps)
        {
            TypeInfo ti = new TypeInfo(DataType, PropertyMapsIgnored);

            foreach (PropertyComparisonInfo pci in Properties)
            {
                PropertyComparisonInfo newPci = new PropertyComparisonInfo(pci.PropertyDescriptor, pci.IgnoreProperty);
                if (!PropertyMapsIgnored && pci.Maps.Count > 0 && clonePropertyMaps)
                {
                    foreach (PropertyMap pm in pci.Maps)
                    {
                        newPci.Maps.Add(pm);
                    }
                }
                ti.Properties.Add(newPci);
            }
            return(ti);
        }
Пример #4
0
 /// <summary>
 /// Creates and returns a list of <see cref="PropertyComparisonInfo"/> objects from a list of
 /// <see cref="PropertyDescriptor"/> objects.
 /// </summary>
 /// <param name="pdColl">The collection of property descriptors.</param>
 /// <param name="ignorePropertyMappings">A Boolean value that indicates if property
 /// mappings defined through attributes should be ignored.</param>
 /// <returns>An enumeration that lists the created <see cref="PropertyComparisonInfo"/> objects.</returns>
 private static IEnumerable <PropertyComparisonInfo> ObtainPropertyInfos(PropertyDescriptorCollection pdColl, bool ignorePropertyMappings)
 {
     foreach (PropertyDescriptor pd in pdColl)
     {
         IgnoreForComparisonAttribute ignoreAtt = pd.Attributes.OfType <IgnoreForComparisonAttribute>().FirstOrDefault();
         PropertyComparisonInfo       pci       = new PropertyComparisonInfo(pd, ignoreAtt?.IgnoreOptions ?? IgnorePropertyOptions.DoNotIgnore);
         if (!ignorePropertyMappings)
         {
             //Obtain maps.
             foreach (PropertyMapAttribute attribute in pd.Attributes.OfType <PropertyMapAttribute>())
             {
                 if (attribute == null)
                 {
                     continue;
                 }
                 pci.Maps.Add(attribute.PropertyMap);
             }
         }
         yield return(pci);
     }
 }
Пример #5
0
        /// <summary>
        /// Creates a <see cref="TypeInfo"/> object with the necessary property information to
        /// compare objects of the specified type.
        /// </summary>
        /// <param name="type">The data type whose type information is requested.</param>
        /// <param name="ignorePropertyMappings">A Boolean value that indicates if property
        /// mappings defined through attributes should be ignored.  If the argument is not
        /// provided, then by default the property mappings are included.</param>
        /// <returns>A <see cref="TypeInfo"/> object that can be used to compare properties.</returns>
        internal static TypeInfo BuildTypeInformation(Type type, bool ignorePropertyMappings = false)
        {
            TypeInfo ti = new TypeInfo(type, ignorePropertyMappings);

#if NET461
            //Obtain property map and ignore property data from MetadataTypeAttribute, if present.
            PropertyComparisonInfoCollection metadataOnlyPropertyInfos = new PropertyComparisonInfoCollection();
            MetadataTypeAttribute            att = type.GetCustomAttribute <MetadataTypeAttribute>();
            if (att != null)
            {
                foreach (PropertyComparisonInfo pi in ObtainPropertyInfos(TypeDescriptor.GetProperties(att.MetadataClassType), ignorePropertyMappings))
                {
                    metadataOnlyPropertyInfos.Add(pi);
                }
            }
#endif
            //Now process regular property descriptors.
            foreach (PropertyComparisonInfo pci in ObtainPropertyInfos(TypeDescriptor.GetProperties(type), ignorePropertyMappings))
            {
#if NET461
                if (!ignorePropertyMappings && metadataOnlyPropertyInfos.Contains(pci.Name))
                {
                    PropertyComparisonInfo mpci = metadataOnlyPropertyInfos[pci.Name];
                    //Combine the IgnoreProperty values.
                    pci.IgnoreProperty |= mpci.IgnoreProperty;
                    //Merge the PropertyMap objects.
                    foreach (PropertyMap pm in mpci.Maps)
                    {
                        if (pci.Maps.Contains(pm.TargetType))
                        {
                            continue;
                        }
                        pci.Maps.Add(pm);
                    }
                }
#endif
                ti.Properties.Add(pci);
            }
            return(ti);
        }
Пример #6
0
        /// <summary>
        /// Compares the property values of the first object against the property values of the
        /// second object according to the preset property mapping rules between the two object
        /// data types.
        /// </summary>
        /// <param name="object1">The first object to be compared against a second object.</param>
        /// <param name="object2">The second object of the comparison operation.</param>
        /// <param name="results">If provided, it will be used to collect the comparison results.</param>
        /// <returns>A Boolean value with the summarized result of the comparison.  True if any
        /// property values were deemed different; false if all property values turned out equal.</returns>
        /// <exception cref="ArgumentNullException">Thrown if either object is null.</exception>
        /// <exception cref="ArgumentException">Thrown if either object is not of the expected
        /// data type.</exception>
        /// <exception cref="InvalidOperationException">Thrown if both objects are really the same
        /// object.</exception>
        public bool Compare(object object1, object object2, ICollection <PropertyComparisonResult> results = null)
        {
            #region Argument Validation
            Guard.RequiredArgument(object1, nameof(object1));
            Guard.RequiredArgument(object2, nameof(object2));
            Guard.ArgumentCondition(
                () => object1.GetType() == Type1, nameof(object1),
                $"The provided object is not of the expected type ({Type1})."
                );
            Guard.ArgumentCondition(
                () => object2.GetType() == Type2, nameof(object2),
                $"The provided object is not of the expected type ({Type2})."
                );
            Guard.Condition(() => !Object.ReferenceEquals(object1, object2), "The objects to compare must be different.");
            #endregion

            bool isDifferent = false;
            foreach (PropertyComparisonInfo pci1 in _typeInfo1.Properties)
            {
                ComparisonResult result = ComparisonResult.Undefined;
                //Obtain the PropertyMapping for this propertyInfo.
                //If none, map by property name.
                PropertyMap mapToUse = null;
                if (pci1.Maps.Contains(Type2))
                {
                    mapToUse = pci1.Maps[Type2];
                }
                object val1 = null;
                object val2 = null;
                PropertyComparisonInfo pci2             = null;
                System.Exception       catchedException = null;
                //Ignore the property if no mapping exists and is being ignored for type 2,
                //or mapping exists and it states the property must be ignored.
                bool propertyIgnored =
                    ((pci1.IgnoreProperty & IgnorePropertyOptions.IgnoreForSelf) == IgnorePropertyOptions.IgnoreForSelf && Type1 == Type2) ||
                    ((pci1.IgnoreProperty & IgnorePropertyOptions.IgnoreForOthers) == IgnorePropertyOptions.IgnoreForOthers && Type1 != Type2);
                if ((mapToUse == null && propertyIgnored) ||
                    (mapToUse?.Operation == PropertyMapOperation.IgnoreProperty))
                {
                    result |= ComparisonResult.PropertyIgnored;
                }
                else
                {
                    string prop2Name = mapToUse?.TargetProperty ?? pci1.Name;
                    //Get the property value of the first object.
                    val1 = pci1.GetValue(object1);
                    if (_typeInfo2.Properties.Contains(prop2Name))
                    {
                        //Get the property value of the second object.
                        pci2 = _typeInfo2.Properties[prop2Name];
                        val2 = pci2.GetValue(object2);
                        //Determine any base type to cover for T? vs T or vice versa.
                        Type p1BaseType = GetNullableUnderlyingType(pci1.PropertyType) ?? pci1.PropertyType;
                        Type p2BaseType = GetNullableUnderlyingType(pci2.PropertyType) ?? pci2.PropertyType;
                        //Determine the comparer to use.
                        IComparer comparer = null;
                        if ((mapToUse?.ForceStringValue ?? false) ||
                            p1BaseType != p2BaseType)
                        {
                            comparer = ResolveComparerForType(typeof(string));
                            try
                            {
                                val1 = ConvertPropertyValueToString(val1, mapToUse?.FormatString);
                                val2 = ConvertPropertyValueToString(val2, mapToUse?.TargetFormatString);
                            }
                            catch (System.Exception ex)
                            {
                                result          |= ComparisonResult.StringCoercionException;
                                catchedException = ex;
                            }
                            result |= ComparisonResult.StringCoercion;
                        }
                        else
                        {
                            comparer = ResolveComparerForType(p1BaseType);
                        }
                        if (catchedException == null)
                        {
                            try
                            {
                                int comp = comparer.Compare(val1, val2);
                                if (comp < 0)
                                {
                                    result |= ComparisonResult.LessThan;
                                }
                                else if (comp > 0)
                                {
                                    result |= ComparisonResult.GreaterThan;
                                }
                                else
                                {
                                    result |= ComparisonResult.Equal;
                                }
                            }
                            catch (System.ArgumentException)
                            {
                                //Property type does not implement IComparable and there is no comparer
                                //registered for the data type.
                                result |= ComparisonResult.NoComparer;
                                //So try to at least find out if it is equal or not.
                                try
                                {
                                    if (Object.Equals(val1, val2))
                                    {
                                        result |= ComparisonResult.Equal;
                                    }
                                    else
                                    {
                                        result |= ComparisonResult.NotEqual;
                                    }
                                }
                                catch (System.Exception ex)
                                {
                                    result          |= ComparisonResult.ComparisonException;
                                    catchedException = ex;
                                }
                            }
                            catch (System.Exception ex)
                            {
                                result          |= ComparisonResult.ComparisonException;
                                catchedException = ex;
                            }
                        }
                    }
                    else
                    {
                        //We are done here since there is no matching property to compare against.
                        result |= ComparisonResult.PropertyNotFound;
                    }
                }
                PropertyComparisonResult pcr = new PropertyComparisonResult(result, pci1, val1, pci2,
                                                                            val2, mapToUse, catchedException);
                results?.Add(pcr);
                isDifferent = isDifferent || ((result & ComparisonResult.NotEqual) ==
                                              ComparisonResult.NotEqual);
            }
            return(isDifferent);
        }