private MemberInitExpression BuildProjectionExpression(Expression sourceAccess, Type destinationType, ProposedTypeMapping proposedMap) { var memberBindings = new List <MemberBinding>(); foreach (var member in proposedMap.ProposedMappings) { BuildMemberAssignmentExpressions(sourceAccess, memberBindings, member, proposedMap.CustomMapping); } foreach (var complexMember in proposedMap.ProposedTypeMappings) { if (complexMember.IsEnumerable || CollectionTypeHelper.IsEnumerable(complexMember)) { if (mapper.Options.Projection.MapCollectionMembers) { BuildCollectionComplexTypeExpression(sourceAccess, memberBindings, complexMember); } } else { BuildComplexTypeExpression(sourceAccess, memberBindings, complexMember); } } var initDestination = Expression.MemberInit(Expression.New(destinationType), memberBindings); return(initDestination); }
private bool CanUseDirectAssignment(TypePair pair, PropertyOrFieldInfo destinationMember, PropertyOrFieldInfo sourceMember, Type nullableType, ProposedHierarchicalMapping hierarchicalMapping) { Type sourceMemberType = null; if (sourceMember == null) { if (hierarchicalMapping == null) { return(false); } } else { sourceMemberType = sourceMember.PropertyOrFieldType; } if (hierarchicalMapping != null) { sourceMemberType = hierarchicalMapping.ReturnType; } if (!destinationMember.PropertyOrFieldType.IsAssignableFrom(sourceMemberType)) { if (sourceMemberType.IsNullableValueType() && destinationMember.PropertyOrFieldType.IsAssignableFrom(nullableType)) { return(true); } else if (ConversionTypeHelper.AreConvertible(sourceMemberType, destinationMember.PropertyOrFieldType)) { return(true); } else { return(false); } } if (pair.SourceType == pair.DestinationType && this.options.Conventions.MakeCloneIfDestinationIsTheSameAsSource) { if (sourceMemberType.IsValueType || sourceMemberType == typeof(string)) { return(true); } else { return(false); } } // Can't assign enumerable memebers to eachother, we leave that decision to the code // generator which can determine if we need to preserve the contents of the enumerable. if (CollectionTypeHelper.IsEnumerable(destinationMember.PropertyOrFieldType) && CollectionTypeHelper.IsEnumerable(sourceMemberType) && this.options.Conventions.PreserveDestinationListContents) // but only if the option is turned on at all { return(false); } return(true); }
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); } }
private static bool AreMembersIEnumerable(PropertyOrFieldInfo destinationMember, PropertyOrFieldInfo sourceMember) { return(CollectionTypeHelper.IsEnumerable(sourceMember.PropertyOrFieldType) && CollectionTypeHelper.IsEnumerable(destinationMember.PropertyOrFieldType)); }
/// <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); }