Example #1
0
        private void BuildCollectionComplexTypeExpression(Expression sourceAccess, List <MemberBinding> memberBindings, ProposedTypeMapping complexMember)
        {
            if (complexMember.Ignored)
            {
                return;
            }

            var typeOfSourceEnumerable = CollectionTypeHelper.GetTypeInsideEnumerable(complexMember.SourceMember.PropertyOrFieldType);

            var genericSourceEnumerable = typeof(IEnumerable <>).MakeGenericType(typeOfSourceEnumerable);

            var typeOfDestEnumerable = CollectionTypeHelper.GetTypeInsideEnumerable(complexMember.DestinationMember.PropertyOrFieldType);

            var genericDestEnumerable = typeof(IEnumerable <>).MakeGenericType(typeOfSourceEnumerable);

            var selectMethod = GetSelectMethod().MakeGenericMethod(typeOfSourceEnumerable, typeOfDestEnumerable);

            var selectParam = Expression.Parameter(typeOfSourceEnumerable, "src");

            var bindings = new List <MemberBinding>();

            var memberInit = BuildProjectionExpression(selectParam, typeOfDestEnumerable, complexMember);

            var funcType = typeof(Func <,>).MakeGenericType(typeOfSourceEnumerable, typeOfDestEnumerable);

            var memberInitLambda = Expression.Lambda(funcType, memberInit, selectParam);

            var accessMember = Expression.MakeMemberAccess(sourceAccess, complexMember.SourceMember);

            var callSelect = Expression.Call(null, selectMethod, accessMember, memberInitLambda);

            Expression finalExpression;

            var conversionMethod = DetermineIEnumerableConversionMethod(complexMember.DestinationMember.PropertyOrFieldType, typeOfSourceEnumerable, typeOfDestEnumerable);

            if (conversionMethod != null)
            {
                finalExpression = Expression.Call(null, conversionMethod, callSelect);
            }
            else
            {
                finalExpression = callSelect;
            }


            var bindSourceToDest = Expression.Bind(complexMember.DestinationMember, finalExpression);

            memberBindings.Add(bindSourceToDest);

            //BuildComplexTypeExpression(selectParam, bindings, complexMember);
        }
            private void GenerateEnumerableMapping(int currentDepth, MemberOptions options, CustomMapping customMapping, ProposedTypeMapping typeMapping, PropertyOrFieldInfo destinationMember, PropertyOrFieldInfo sourceMember)
            {
                var typeOfSourceEnumerable      = CollectionTypeHelper.GetTypeInsideEnumerable(sourceMember.PropertyOrFieldType);
                var typeOfDestinationEnumerable = CollectionTypeHelper.GetTypeInsideEnumerable(destinationMember.PropertyOrFieldType);

                var canAssignSourceItemsToDestination = CanAssignSourceItemsToDestination(destinationMember, sourceMember, typeOfSourceEnumerable, typeOfDestinationEnumerable);

                if (canAssignSourceItemsToDestination)
                {
                    typeMapping.ProposedTypeMappings.Add(
                        new ProposedTypeMapping
                    {
                        DestinationMember = destinationMember,
                        SourceMember      = sourceMember,
                        ProposedMappings  = new List <ProposedMemberMapping>()
                    });
                }
                else
                {
                    var complexPair = new TypePair(typeOfSourceEnumerable, typeOfDestinationEnumerable);

                    var complexTypeMapping = GetComplexTypeMapping(currentDepth + 1, complexPair, options, customMapping);

                    if (complexTypeMapping != null)
                    {
                        complexTypeMapping = complexTypeMapping.Clone();

                        complexTypeMapping.DestinationMember = destinationMember;
                        complexTypeMapping.SourceMember      = sourceMember;

                        CustomMapping customMappingForType;

                        TryGetCustomMapping(complexPair, out customMappingForType);

                        complexTypeMapping.CustomMapping = customMappingForType;

                        typeMapping.ProposedTypeMappings.Add(complexTypeMapping);
                    }
                    else
                    {
                        typeMapping.DoNotCache = true;
                    }
                }
            }
            private bool TryGetCustomMapping(TypePair pair, out CustomMapping customMapping)
            {
                if (CollectionTypeHelper.IsEnumerable(pair))
                {
                    var source      = CollectionTypeHelper.GetTypeInsideEnumerable(pair.SourceType);
                    var destination = CollectionTypeHelper.GetTypeInsideEnumerable(pair.DestinationType);
                    pair = new TypePair(source, destination);
                }
                IEnumerable <CustomMapping> matchingMappings;

                customMappingCache.TryGetValue(pair, out customMapping);

                matchingMappings = (from m in customMappingCache
                                    where m.Key.DestinationType.IsAssignableFrom(pair.DestinationType) &&
                                    m.Key.DestinationType != pair.DestinationType
                                    orderby DistanceFromType(pair.DestinationType, m.Key.DestinationType, 0) ascending
                                    select m.Value);

                matchingMappings = matchingMappings.Union(from m in customMappingCache
                                                          where m.Key.SourceType.IsAssignableFrom(pair.SourceType) &&
                                                          m.Key.SourceType != pair.SourceType
                                                          orderby DistanceFromType(pair.SourceType, m.Key.SourceType, 0) ascending
                                                          select m.Value).ToList();


                customMapping = customMapping ?? matchingMappings.FirstOrDefault();

                if (customMapping != null)
                {
                    customMapping.CombineWithOtherCustomMappings(matchingMappings);

                    return(true);
                }
                else
                {
                    return(false);
                }
            }
            /// <summary>
            /// Returns a type mapping of the TypePair you pass in.
            /// </summary>
            /// <returns></returns>
            private ProposedTypeMapping GetTypeMapping(int currentDepth, TypePair pair, MemberOptions options = null, CustomMapping customMapping = null)
            {
                if (!typeStack.Contains(pair))
                {
                    typeStack.Push(pair);
                }
                else if (this.options.Safety.IfRecursiveRelationshipIsDetected == RecursivePropertyOptions.ThrowIfRecursionIsDetected)
                {
                    // Oh noes, recursion!
                    throw new RecursiveRelationshipException(pair);
                }
                // if it's a recursive relationship, by default we return null which is handled after the method returns
                else
                {
                    return(null);
                }

                var typeMapping = new ProposedTypeMapping();

                typeMapping.SourceMember      = null;
                typeMapping.DestinationMember = null;

                Type destinationType, sourceType;

                // If it's an enumerable type (List<>, IEnumerable<>, etc) then we're currently interested
                // in the type 'inside' the enumerable.
                if (CollectionTypeHelper.IsEnumerable(pair.DestinationType))
                {
                    destinationType = CollectionTypeHelper.GetTypeInsideEnumerable(pair.DestinationType);
                }
                else
                {
                    destinationType = pair.DestinationType;
                }

                // Same here.
                if (CollectionTypeHelper.IsEnumerable(pair.SourceType))
                {
                    sourceType = CollectionTypeHelper.GetTypeInsideEnumerable(pair.SourceType);
                }
                else
                {
                    sourceType = pair.SourceType;
                }

                // The memberprovider is responsible for linking a destination member with a source member
                var memberProvider = this.strategy.MemberProviderFactory.GetMemberProvider(sourceType, destinationType, mapper);

                // Loop through all members it could find
                foreach (var mapping in GetTypeMembers(memberProvider, options, currentDepth))
                {
                    var destinationMember = mapping.Destination;
                    var sourceMember      = mapping.Source;

                    // Does the memberprovider see any reason to ignore this member?
                    if (memberProvider.IsMemberIgnored(sourceType, destinationMember))
                    {
                        continue;
                    }


                    Expression customExpression = null;

                    // Try to extract an expression that was supplied for this destination member
                    if (customMapping != null)
                    {
                        customExpression = customMapping.GetExpressionForMember(destinationMember);

                        if (mapping.ConversionFunction == null)
                        {
                            var conversionFunction = customMapping.GetConversionFunction(sourceMember, destinationMember);

                            mapping.ConversionFunction = conversionFunction;
                        }
                    }


                    // Did the user supply a function to transform the source member's value?
                    if (mapping.ConversionFunction != null)
                    {
                        // If no custom mapping yet, then we need to create one
                        // as it's where we'll be storing the conversion function
                        if (customMapping == null)
                        {
                            customMapping = new CustomMapping
                            {
                                DestinationType = destinationType
                            };

                            customMappingCache.AddOrUpdate(pair, customMapping, (k, v) => customMapping);

                            typeMapping.CustomMapping = customMapping;
                        }

                        // Let the custom mapping be the owner of the conversion function
                        customMapping.AddConversionFunction(sourceMember, destinationMember, mapping.ConversionFunction);
                    }
                    else if (customMapping != null)
                    {
                    }

                    ProposedHierarchicalMapping hierarchicalMapping = null;

                    // No source member or can't write to the destination?
                    if (HasNoSourceMember(customExpression, sourceMember) || !destinationMember.CanWrite)
                    {
                        if (this.options.Conventions.AutomaticallyFlattenHierarchies)
                        {
                            // Propose a mapping that flattens a hierarchy if possible.
                            // For example, map type.CompanyName to otherType.Company.Name
                            hierarchicalMapping = memberProvider.ProposeHierarchicalMapping(destinationMember);
                        }

                        // No way to map this thing? Add it to incompatible members if the option has been turned on.
                        // Will cause an (intended) exception later on, allowing you to verify your mappings
                        // for correctness and completeness.
                        if (hierarchicalMapping == null && this.options.Strictness.ThrowWithoutCorrespondingSourceMember)
                        {
                            typeMapping.IncompatibleMappings.Add(destinationMember);
                        }
                    }

                    // Nullable value types screw up everything!
                    var nullableType = NullableTypeHelper.TryGetNullableType(sourceMember);

                    // Can we do a simple right to left assignment between the members?
                    // So, are they basically the same type or do we need to do further mapping?
                    var canUseSimpleTypeMapping = CanUseDirectAssignment(pair, destinationMember, sourceMember, nullableType, hierarchicalMapping);

                    if (canUseSimpleTypeMapping)
                    {
                        // If simple mapping is possible create a mapping between the members
                        typeMapping.ProposedMappings.Add
                        (
                            new ProposedMemberMapping
                        {
                            SourceMember        = sourceMember,
                            DestinationMember   = destinationMember,
                            HierarchicalMapping = hierarchicalMapping
                        }
                        );
                    }
                    // No simple assignment, but a custom expression is supplied
                    // and that's just as good as having a direct assignment mapping
                    else if (customExpression != null || mapping.ConversionFunction != null)
                    {
                        typeMapping.ProposedMappings.Add
                        (
                            new ProposedMemberMapping
                        {
                            SourceMember        = sourceMember,
                            DestinationMember   = destinationMember,
                            HierarchicalMapping = hierarchicalMapping
                        }
                        );
                    }
                    // We have a source member but can't directly assign the source to the destination.
                    // Further mapping is needed.
                    else if (sourceMember != null)
                    {
                        // Is the member of an IEnumerable type?
                        if (AreMembersIEnumerable(destinationMember, sourceMember))
                        {
                            // Create a deeper mapping for IEnumerable members
                            GenerateEnumerableMapping(currentDepth, options, customMapping, typeMapping, destinationMember, sourceMember);
                        }
                        else
                        {
                            // Create a deeper mapping for a 'regular' type.
                            GenerateComplexTypeMapping(currentDepth, options, customMapping, typeMapping, destinationMember, sourceMember);
                        }
                    }
                    // All we have is a destination member and a custom expression
                    // that gives the destination member a value. Good enough!
                    else if (customExpression != null)
                    {
                        typeMapping.ProposedMappings.Add
                        (
                            new ProposedMemberMapping
                        {
                            SourceMember      = null,
                            DestinationMember = destinationMember
                        }
                        );
                    }
                }

                // Don't cache the typemapping when this flag has been set.
                // That happens when the maximum depth was reached during the mapping
                // for this particular type and we didn't explore the full depth
                // of the type. We don't wanna reuse this typemapping at a later time
                // because the mapping might be for something completely different
                // at a depth at which the full depth CAN be explored.
                if (!typeMapping.DoNotCache)
                {
                    mappingCache[pair] = typeMapping;
                }

                typeStack.Pop();

                return(typeMapping);
            }