@@ -30,6 +30,9 @@ public XpressionMapperVisitor(IMapper mapper, IConfigurationProvider configurati
30
30
31
31
protected IMapper Mapper { get ; }
32
32
33
+ private IConfigurationProvider anonymousTypesConfigurationProvider ;
34
+ private Configuration . MapperConfigurationExpression anonymousTypesBaseMappings = new Configuration . MapperConfigurationExpression ( ) ;
35
+
33
36
protected override Expression VisitParameter ( ParameterExpression node )
34
37
{
35
38
InfoDictionary . Add ( node , TypeMappings ) ;
@@ -172,7 +175,7 @@ protected override Expression VisitNew(NewExpression node)
172
175
for ( int i = 0 ; i < parameters . Length ; i ++ )
173
176
bindingExpressions . Add ( parameters [ i ] . Name , this . Visit ( node . Arguments [ i ] ) ) ;
174
177
175
- return GetMemberInitExpression ( bindingExpressions , node . Type ) ;
178
+ return GetAnonymousTypeMemberInitExpression ( bindingExpressions , node . Type ) ;
176
179
}
177
180
178
181
return base . VisitNew ( node ) ;
@@ -196,7 +199,7 @@ protected override Expression VisitMemberInit(MemberInitExpression node)
196
199
{
197
200
if ( this . TypeMappings . TryGetValue ( node . Type , out Type newType ) )
198
201
{
199
- var typeMap = ConfigurationProvider . CheckIfMapExists ( sourceType : newType , destinationType : node . Type ) ;
202
+ var typeMap = ConfigurationProvider . CheckIfTypeMapExists ( sourceType : newType , destinationType : node . Type ) ;
200
203
//The destination becomes the source because to map a source expression to a destination expression,
201
204
//we need the expressions used to create the source from the destination
202
205
@@ -221,7 +224,7 @@ protected override Expression VisitMemberInit(MemberInitExpression node)
221
224
}
222
225
else if ( IsAnonymousType ( node . Type ) )
223
226
{
224
- return GetMemberInitExpression
227
+ return GetAnonymousTypeMemberInitExpression
225
228
(
226
229
node . Bindings
227
230
. OfType < MemberAssignment > ( )
@@ -237,11 +240,36 @@ protected override Expression VisitMemberInit(MemberInitExpression node)
237
240
return base . VisitMemberInit ( node ) ;
238
241
}
239
242
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 )
241
268
{
242
269
Type newAnonymousType = AnonymousTypeFactory . CreateAnonymousType ( bindingExpressions . ToDictionary ( a => a . Key , a => a . Value . Type ) ) ;
243
270
TypeMappings . AddTypeMapping ( ConfigurationProvider , oldType , newAnonymousType ) ;
244
271
272
+ ConfigureAnonymousTypeMaps ( oldType , newAnonymousType ) ;
245
273
return Expression . MemberInit
246
274
(
247
275
Expression . New ( newAnonymousType ) ,
@@ -593,7 +621,7 @@ protected void FindDestinationFullName(Type typeSource, Type typeDestination, st
593
621
bool BothTypesAreAnonymous ( )
594
622
=> IsAnonymousType ( typeSource ) && IsAnonymousType ( typeDestination ) ;
595
623
596
- if ( typeSource == typeDestination || BothTypesAreAnonymous ( ) )
624
+ if ( typeSource == typeDestination )
597
625
{
598
626
var sourceFullNameArray = sourceFullName . Split ( new [ ] { period [ 0 ] } , StringSplitOptions . RemoveEmptyEntries ) ;
599
627
sourceFullNameArray . Aggregate ( propertyMapInfoList , ( list , next ) =>
@@ -644,7 +672,10 @@ bool BothTypesAreAnonymous()
644
672
}
645
673
}
646
674
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,
648
679
//we need the expressions used to create the source from the destination
649
680
650
681
PathMap pathMap = typeMap . FindPathMapByDestinationPath ( destinationFullPath : sourceFullName ) ;
@@ -657,8 +688,8 @@ bool BothTypesAreAnonymous()
657
688
658
689
if ( sourceFullName . IndexOf ( period , StringComparison . OrdinalIgnoreCase ) < 0 )
659
690
{
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 ( ) ) ;
662
693
if ( propertyMap . ValueResolverConfig != null )
663
694
{
664
695
throw new InvalidOperationException ( Resource . customResolversNotSupported ) ;
@@ -678,7 +709,7 @@ void CompareSourceAndDestLiterals(Type mappedPropertyType, string mappedProperty
678
709
{
679
710
//switch from IsValueType to IsLiteralType because we do not want to throw an exception for all structs
680
711
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 ( ) ) ) ;
682
713
}
683
714
684
715
if ( propertyMap . ProjectToCustomSource != null )
@@ -689,9 +720,9 @@ void CompareSourceAndDestLiterals(Type mappedPropertyType, string mappedProperty
689
720
else
690
721
{
691
722
var propertyName = sourceFullName . Substring ( 0 , sourceFullName . IndexOf ( period , StringComparison . OrdinalIgnoreCase ) ) ;
692
- var propertyMap = typeMap . GetPropertyMapByDestinationProperty ( propertyName ) ;
723
+ var propertyMap = typeMap . GetMemberMapByDestinationProperty ( propertyName ) ;
693
724
694
- var sourceMemberInfo = typeSource . GetFieldOrProperty ( propertyMap . DestinationMember . Name ) ;
725
+ var sourceMemberInfo = typeSource . GetFieldOrProperty ( propertyMap . GetDestinationName ( ) ) ;
695
726
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.
696
727
throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Resource . srcMemberCannotBeNullFormat , typeSource . Name , typeDestination . Name , propertyName ) ) ;
697
728
0 commit comments