/// <summary> /// Returns true if object name should be ignored /// </summary> /// <param name="name">Property or field name</param> /// <param name="path">Full path to object</param> /// <param name="options">Comparison options</param> /// <param name="ignorePropertiesOrPaths">List of names or paths to ignore</param> /// <returns></returns> private bool IgnoreObjectName(string name, string path, MappingOptions options, ICollection <string> ignorePropertiesOrPaths, IEnumerable <CustomAttributeData> attributes = null) { var ignoreByNameOrPath = ignorePropertiesOrPaths?.Contains(name) == true || ignorePropertiesOrPaths?.Contains(path) == true; if (ignoreByNameOrPath) { return(true); } #if FEATURE_CUSTOM_ATTRIBUTES if (attributes?.Any(x => !options.BitwiseHasFlag(MappingOptions.DisableIgnoreAttributes) && (_ignoreAttributes.Contains(x.AttributeType) || _ignoreAttributes.Contains(x.AttributeType.Name))) == true) #else if (attributes?.Any(x => !options.BitwiseHasFlag(MappingOptions.DisableIgnoreAttributes) && (_ignoreAttributes.Contains(x.Constructor.DeclaringType) || _ignoreAttributes.Contains(x.Constructor.DeclaringType.Name))) == true) #endif { return(true); } return(false); }
/// <summary> /// Initialize the mapper and scan for profiles /// </summary> /// <param name="options"></param> public static void Initialize(MappingOptions options = MappingOptions.ScanCurrentAssembly) { var type = typeof(Profile); if (options.BitwiseHasFlag(MappingOptions.ScanAllAssemblies)) { // scan all known assemblies in the app domain Initialize(AppDomain.CurrentDomain.GetAssemblies()); } else { // scan the current calling assembly for profiles Initialize(new Assembly[] { Assembly.GetCallingAssembly() }); } }
/// <summary> /// (Recursive) Recursive function that inspects an object and its properties/fields and clones it /// </summary> /// <param name="sourceObject">The object to clone</param> /// <param name="destObject">The destination object</param> /// <param name="mapToType">The type to map to</param> /// <param name="currentDepth">The current tree depth</param> /// <param name="maxDepth">The max tree depth</param> /// <param name="options">The cloning options</param> /// <param name="objectTree">The object tree to prevent cyclical references</param> /// <param name="path">The current path being traversed</param> /// <param name="ignorePropertiesOrPaths">A list of properties or paths to ignore</param> /// <returns></returns> private object InspectAndMap <TSource, TDest>(object sourceObject, object destObject, ExtendedType mapToType, int currentDepth, int maxDepth, MappingOptions options, IDictionary <ObjectHashcode, object> objectTree, string path, ICollection <string> ignorePropertiesOrPaths = null) { if (IgnoreObjectName(null, path, options, ignorePropertiesOrPaths)) { return(null); } if (sourceObject == null) { return(null); } // ensure we don't go too deep if specified if (maxDepth > 0 && currentDepth >= maxDepth) { return(null); } var sourceType = typeof(TSource).GetExtendedType(); var destType = typeof(TDest).GetExtendedType(); if (ignorePropertiesOrPaths == null) { ignorePropertiesOrPaths = new List <string>(); } // drop any objects we are ignoring by attribute if (mapToType.Attributes.Any(x => _ignoreAttributes.Contains(x)) && options.BitwiseHasFlag(MappingOptions.DisableIgnoreAttributes)) { return(null); } // for delegate types, copy them by reference rather than returning null if (mapToType.IsDelegate) { return(sourceObject); } object newObject = destObject; // create a new empty object of the desired type if (newObject == null) { if (mapToType.IsArray) { var length = 0; if (mapToType.IsArray) { length = (sourceObject as Array).Length; } newObject = _objectFactory.CreateEmptyObject(mapToType.Type, length); } else if (mapToType.Type == typeof(string)) { // copy the item directly newObject = Convert.ToString(sourceObject); return(newObject); } else { newObject = _objectFactory.CreateEmptyObject(mapToType.Type); } } if (newObject == null) { return(newObject); } // increment the current recursion depth currentDepth++; // construct a hashtable of objects we have already inspected (simple recursion loop preventer) // we use this hashcode method as it does not use any custom hashcode handlers the object might implement if (sourceObject != null && !mapToType.IsValueType) { var hashCode = System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(sourceObject); var key = new ObjectHashcode(hashCode, newObject.GetType()); if (objectTree.ContainsKey(key)) { return(objectTree[key]); } // ensure we can refer back to the reference for this object objectTree.Add(key, newObject); } var objectMapper = TypeRegistry.ObjectMappings .FirstOrDefault(x => x.SourceObjectType == sourceType.Type && x.DestinationObjectType == destType.Type); try { // clone a dictionary's key/values if (mapToType.IsDictionary && mapToType.IsGeneric) { var genericType = mapToType.Type.GetGenericArguments().ToList(); Type[] typeArgs = { genericType[0], genericType[1] }; var listType = typeof(Dictionary <,>).MakeGenericType(typeArgs); var newDictionary = Activator.CreateInstance(listType) as IDictionary; newObject = newDictionary; var enumerator = (IDictionary)sourceObject; foreach (DictionaryEntry item in enumerator) { var key = InspectAndMap <TSource, TDest>(item.Key, null, item.Key.GetExtendedType(), currentDepth, maxDepth, options, objectTree, path, ignorePropertiesOrPaths); var value = InspectAndMap <TSource, TDest>(item.Value, null, item.Value.GetExtendedType(), currentDepth, maxDepth, options, objectTree, path, ignorePropertiesOrPaths); newDictionary.Add(key, value); } return(newObject); } // clone an enumerables' elements if (mapToType.IsEnumerable && mapToType.IsGeneric) { var genericType = mapToType.Type.GetGenericArguments().First(); var genericExtendedType = genericType.GetExtendedType(); var addMethod = mapToType.Type.GetMethod("Add"); var enumerator = (IEnumerable)sourceObject; foreach (var item in enumerator) { var element = InspectAndMap <TSource, TDest>(item, null, genericExtendedType, currentDepth, maxDepth, options, objectTree, path, ignorePropertiesOrPaths); addMethod.Invoke(newObject, new object[] { element }); } return(newObject); } // clone an arrays' elements if (mapToType.IsArray) { var sourceArray = sourceObject as Array; var newArray = newObject as Array; newObject = newArray; for (var i = 0; i < sourceArray.Length; i++) { var element = sourceArray.GetValue(i); var newElement = InspectAndMap <TSource, TDest>(element, null, mapToType.ElementType.GetExtendedType(), currentDepth, maxDepth, options, objectTree, path, ignorePropertiesOrPaths); newArray.SetValue(newElement, i); } return(newArray); } var fields = sourceObject.GetFields(FieldOptions.AllWritable); var properties = sourceObject.GetProperties(PropertyOptions.HasGetter); var rootPath = path; // clone and recurse fields if (newObject != null) { foreach (var field in fields) { path = $"{rootPath}.{field.Name}"; if (IgnoreObjectName(field.Name, path, options, ignorePropertiesOrPaths, field.CustomAttributes)) { continue; } // also check the property for ignore, if this is a auto-backing property if (field.BackedProperty != null && IgnoreObjectName(field.BackedProperty.Name, $"{rootPath}.{field.BackedPropertyName}", options, ignorePropertiesOrPaths, field.BackedProperty.CustomAttributes)) { continue; } newObject = MapField <TSource, TDest>(newObject, sourceObject, objectMapper, field, currentDepth, maxDepth, options, objectTree, path, ignorePropertiesOrPaths); } foreach (var property in properties) { path = $"{rootPath}.{property.Name}"; if (IgnoreObjectName(property.Name, path, options, ignorePropertiesOrPaths, property.CustomAttributes)) { continue; } // also check the backing field for ignore, if this is a auto-backing property if (property.BackingFieldName != null && IgnoreObjectName(property.BackingFieldName, $"{rootPath}.{property.BackingFieldName}", options, ignorePropertiesOrPaths, fields.FirstOrDefault(x => x.Name == property.BackingFieldName).CustomAttributes)) { continue; } if (string.IsNullOrEmpty(property.BackingFieldName)) { // map the property, it has no backing field so it's likely a method call newObject = MapProperty <TSource, TDest>(newObject, sourceObject, objectMapper, property, currentDepth, maxDepth, options, objectTree, path, ignorePropertiesOrPaths); } } } return(newObject); } finally { } }