@@ -30,6 +30,9 @@ public XpressionMapperVisitor(IMapper mapper, IConfigurationProvider configurati
3030
3131 protected IMapper Mapper { get ; }
3232
33+ private IConfigurationProvider anonymousTypesConfigurationProvider ;
34+ private Configuration . MapperConfigurationExpression anonymousTypesBaseMappings = new Configuration . MapperConfigurationExpression ( ) ;
35+
3336 protected override Expression VisitParameter ( ParameterExpression node )
3437 {
3538 InfoDictionary . Add ( node , TypeMappings ) ;
@@ -172,7 +175,7 @@ protected override Expression VisitNew(NewExpression node)
172175 for ( int i = 0 ; i < parameters . Length ; i ++ )
173176 bindingExpressions . Add ( parameters [ i ] . Name , this . Visit ( node . Arguments [ i ] ) ) ;
174177
175- return GetMemberInitExpression ( bindingExpressions , node . Type ) ;
178+ return GetAnonymousTypeMemberInitExpression ( bindingExpressions , node . Type ) ;
176179 }
177180
178181 return base . VisitNew ( node ) ;
@@ -196,7 +199,7 @@ protected override Expression VisitMemberInit(MemberInitExpression node)
196199 {
197200 if ( this . TypeMappings . TryGetValue ( node . Type , out Type newType ) )
198201 {
199- var typeMap = ConfigurationProvider . CheckIfMapExists ( sourceType : newType , destinationType : node . Type ) ;
202+ var typeMap = ConfigurationProvider . CheckIfTypeMapExists ( sourceType : newType , destinationType : node . Type ) ;
200203 //The destination becomes the source because to map a source expression to a destination expression,
201204 //we need the expressions used to create the source from the destination
202205
@@ -221,7 +224,7 @@ protected override Expression VisitMemberInit(MemberInitExpression node)
221224 }
222225 else if ( IsAnonymousType ( node . Type ) )
223226 {
224- return GetMemberInitExpression
227+ return GetAnonymousTypeMemberInitExpression
225228 (
226229 node . Bindings
227230 . OfType < MemberAssignment > ( )
@@ -237,11 +240,36 @@ protected override Expression VisitMemberInit(MemberInitExpression node)
237240 return base . VisitMemberInit ( node ) ;
238241 }
239242
240- private MemberInitExpression GetMemberInitExpression ( Dictionary < string , Expression > bindingExpressions , Type oldType )
243+ private void ConfigureAnonymousTypeMaps ( Type oldType , Type newAnonymousType )
244+ {
245+ anonymousTypesBaseMappings . CreateMap ( newAnonymousType , oldType ) ;
246+ Dictionary < Type , Type > memberTypeMaps = new Dictionary < Type , Type > ( ) ;
247+ newAnonymousType . GetMembers ( )
248+ . OfType < PropertyInfo > ( )
249+ . ToList ( )
250+ . ForEach ( member =>
251+ {
252+ Type sourceType = member . GetMemberType ( ) ;
253+ Type destMember = oldType . GetMember ( member . Name ) [ 0 ] . GetMemberType ( ) ;
254+ if ( sourceType == destMember )
255+ return ;
256+
257+ if ( ! memberTypeMaps . ContainsKey ( sourceType ) )
258+ {
259+ memberTypeMaps . Add ( sourceType , destMember ) ;
260+ anonymousTypesBaseMappings . CreateMap ( sourceType , destMember ) ;
261+ }
262+ } ) ;
263+
264+ anonymousTypesConfigurationProvider = new MapperConfiguration ( anonymousTypesBaseMappings ) ;
265+ }
266+
267+ private MemberInitExpression GetAnonymousTypeMemberInitExpression ( Dictionary < string , Expression > bindingExpressions , Type oldType )
241268 {
242269 Type newAnonymousType = AnonymousTypeFactory . CreateAnonymousType ( bindingExpressions . ToDictionary ( a => a . Key , a => a . Value . Type ) ) ;
243270 TypeMappings . AddTypeMapping ( ConfigurationProvider , oldType , newAnonymousType ) ;
244271
272+ ConfigureAnonymousTypeMaps ( oldType , newAnonymousType ) ;
245273 return Expression . MemberInit
246274 (
247275 Expression . New ( newAnonymousType ) ,
@@ -593,7 +621,7 @@ protected void FindDestinationFullName(Type typeSource, Type typeDestination, st
593621 bool BothTypesAreAnonymous ( )
594622 => IsAnonymousType ( typeSource ) && IsAnonymousType ( typeDestination ) ;
595623
596- if ( typeSource == typeDestination || BothTypesAreAnonymous ( ) )
624+ if ( typeSource == typeDestination )
597625 {
598626 var sourceFullNameArray = sourceFullName . Split ( new [ ] { period [ 0 ] } , StringSplitOptions . RemoveEmptyEntries ) ;
599627 sourceFullNameArray . Aggregate ( propertyMapInfoList , ( list , next ) =>
@@ -644,7 +672,10 @@ bool BothTypesAreAnonymous()
644672 }
645673 }
646674
647- var typeMap = ConfigurationProvider . CheckIfMapExists ( sourceType : typeDestination , destinationType : typeSource ) ; //The destination becomes the source because to map a source expression to a destination expression,
675+ var typeMap = BothTypesAreAnonymous ( )
676+ ? anonymousTypesConfigurationProvider . CheckIfTypeMapExists ( sourceType : typeDestination , destinationType : typeSource )
677+ : ConfigurationProvider . CheckIfTypeMapExists ( sourceType : typeDestination , destinationType : typeSource ) ;
678+ //The destination becomes the source because to map a source expression to a destination expression,
648679 //we need the expressions used to create the source from the destination
649680
650681 PathMap pathMap = typeMap . FindPathMapByDestinationPath ( destinationFullPath : sourceFullName ) ;
@@ -657,8 +688,8 @@ bool BothTypesAreAnonymous()
657688
658689 if ( sourceFullName . IndexOf ( period , StringComparison . OrdinalIgnoreCase ) < 0 )
659690 {
660- var propertyMap = typeMap . GetPropertyMapByDestinationProperty ( sourceFullName ) ;
661- var sourceMemberInfo = typeSource . GetFieldOrProperty ( propertyMap . DestinationMember . Name ) ;
691+ var propertyMap = typeMap . GetMemberMapByDestinationProperty ( sourceFullName ) ;
692+ var sourceMemberInfo = typeSource . GetFieldOrProperty ( propertyMap . GetDestinationName ( ) ) ;
662693 if ( propertyMap . ValueResolverConfig != null )
663694 {
664695 throw new InvalidOperationException ( Resource . customResolversNotSupported ) ;
@@ -678,7 +709,7 @@ void CompareSourceAndDestLiterals(Type mappedPropertyType, string mappedProperty
678709 {
679710 //switch from IsValueType to IsLiteralType because we do not want to throw an exception for all structs
680711 if ( ( mappedPropertyType . IsLiteralType ( ) || sourceMemberType . IsLiteralType ( ) ) && sourceMemberType != mappedPropertyType )
681- throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Resource . expressionMapValueTypeMustMatchFormat , mappedPropertyType . Name , mappedPropertyDescription , sourceMemberType . Name , propertyMap . DestinationMember . Name ) ) ;
712+ throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Resource . expressionMapValueTypeMustMatchFormat , mappedPropertyType . Name , mappedPropertyDescription , sourceMemberType . Name , propertyMap . GetDestinationName ( ) ) ) ;
682713 }
683714
684715 if ( propertyMap . ProjectToCustomSource != null )
@@ -689,9 +720,9 @@ void CompareSourceAndDestLiterals(Type mappedPropertyType, string mappedProperty
689720 else
690721 {
691722 var propertyName = sourceFullName . Substring ( 0 , sourceFullName . IndexOf ( period , StringComparison . OrdinalIgnoreCase ) ) ;
692- var propertyMap = typeMap . GetPropertyMapByDestinationProperty ( propertyName ) ;
723+ var propertyMap = typeMap . GetMemberMapByDestinationProperty ( propertyName ) ;
693724
694- var sourceMemberInfo = typeSource . GetFieldOrProperty ( propertyMap . DestinationMember . Name ) ;
725+ var sourceMemberInfo = typeSource . GetFieldOrProperty ( propertyMap . GetDestinationName ( ) ) ;
695726 if ( propertyMap . CustomMapExpression == null && ! propertyMap . SourceMembers . Any ( ) ) //If sourceFullName has a period then the SourceMember cannot be null. The SourceMember is required to find the ProertyMap of its child object.
696727 throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Resource . srcMemberCannotBeNullFormat , typeSource . Name , typeDestination . Name , propertyName ) ) ;
697728
0 commit comments