55using static System . Linq . Expressions . Expression ;
66using System . Reflection ;
77using AutoMapper . Mappers . Internal ;
8+ using AutoMapper . Internal ;
89
910namespace AutoMapper . Extensions . ExpressionMapping
1011{
@@ -21,22 +22,60 @@ public static TDestDelegate MapExpression<TDestDelegate>(this IMapper mapper, La
2122 where TDestDelegate : LambdaExpression
2223 {
2324 if ( expression == null )
24- return default ( TDestDelegate ) ;
25+ return default ;
2526
26- var typeSourceFunc = expression . GetType ( ) . GetGenericArguments ( ) [ 0 ] ;
27- var typeDestFunc = typeof ( TDestDelegate ) . GetGenericArguments ( ) [ 0 ] ;
27+ return mapper . MapExpression < TDestDelegate >
28+ (
29+ expression ,
30+ ( config , mappings ) => new XpressionMapperVisitor ( mapper , config , mappings )
31+ ) ;
32+ }
33+
34+ private static TDestDelegate MapExpression < TDestDelegate > ( this IMapper mapper , LambdaExpression expression , Func < IConfigurationProvider , Dictionary < Type , Type > , XpressionMapperVisitor > getVisitor )
35+ where TDestDelegate : LambdaExpression
36+ {
37+ return MapExpression < TDestDelegate >
38+ (
39+ mapper ?? Mapper . Instance ,
40+ mapper == null ? Mapper . Configuration : mapper . ConfigurationProvider ,
41+ expression ,
42+ expression . GetType ( ) . GetGenericArguments ( ) [ 0 ] ,
43+ typeof ( TDestDelegate ) . GetGenericArguments ( ) [ 0 ] ,
44+ getVisitor
45+ ) ;
46+ }
2847
29- var typeMappings = new Dictionary < Type , Type > ( )
30- . AddTypeMappingsFromDelegates ( typeSourceFunc , typeDestFunc ) ;
48+ private static TDestDelegate MapExpression < TDestDelegate > ( IMapper mapper ,
49+ IConfigurationProvider configurationProvider ,
50+ LambdaExpression expression ,
51+ Type typeSourceFunc ,
52+ Type typeDestFunc ,
53+ Func < IConfigurationProvider , Dictionary < Type , Type > , XpressionMapperVisitor > getVisitor )
54+ where TDestDelegate : LambdaExpression
55+ {
56+ return CreateVisitor ( new Dictionary < Type , Type > ( ) . AddTypeMappingsFromDelegates ( configurationProvider , typeSourceFunc , typeDestFunc ) ) ;
57+
58+ TDestDelegate CreateVisitor ( Dictionary < Type , Type > typeMappings )
59+ => MapBody ( typeMappings , getVisitor ( configurationProvider , typeMappings ) ) ;
3160
32- var visitor = new XpressionMapperVisitor ( mapper == null ? Mapper . Configuration : mapper . ConfigurationProvider , typeMappings ) ;
33- var remappedBody = visitor . Visit ( expression . Body ) ;
34- if ( remappedBody == null )
35- throw new InvalidOperationException ( Resource . cantRemapExpression ) ;
61+ TDestDelegate MapBody ( Dictionary < Type , Type > typeMappings , XpressionMapperVisitor visitor )
62+ => GetLambda ( typeMappings , visitor , visitor . Visit ( expression . Body ) ) ;
3663
37- return ( TDestDelegate ) Lambda ( typeDestFunc , remappedBody , expression . GetDestinationParameterExpressions ( visitor . InfoDictionary , typeMappings ) ) ;
64+ TDestDelegate GetLambda ( Dictionary < Type , Type > typeMappings , XpressionMapperVisitor visitor , Expression remappedBody )
65+ {
66+ if ( remappedBody == null )
67+ throw new InvalidOperationException ( Resource . cantRemapExpression ) ;
68+
69+ return ( TDestDelegate ) Lambda
70+ (
71+ typeDestFunc ,
72+ ExpressionFactory . ToType ( remappedBody , typeDestFunc . GetGenericArguments ( ) . Last ( ) ) ,
73+ expression . GetDestinationParameterExpressions ( visitor . InfoDictionary , typeMappings )
74+ ) ;
75+ }
3876 }
3977
78+
4079 /// <summary>
4180 /// Maps an expression given a dictionary of types where the source type is the key and the destination type is the value.
4281 /// </summary>
@@ -47,7 +86,7 @@ public static TDestDelegate MapExpression<TDestDelegate>(this IMapper mapper, La
4786 /// <returns></returns>
4887 public static TDestDelegate MapExpression < TSourceDelegate , TDestDelegate > ( this IMapper mapper , TSourceDelegate expression )
4988 where TSourceDelegate : LambdaExpression
50- where TDestDelegate : LambdaExpression
89+ where TDestDelegate : LambdaExpression
5190 => mapper . MapExpression < TDestDelegate > ( expression ) ;
5291
5392 /// <summary>
@@ -61,20 +100,13 @@ public static TDestDelegate MapExpressionAsInclude<TDestDelegate>(this IMapper m
61100 where TDestDelegate : LambdaExpression
62101 {
63102 if ( expression == null )
64- return null ;
65-
66- var typeSourceFunc = expression . GetType ( ) . GetGenericArguments ( ) [ 0 ] ;
67- var typeDestFunc = typeof ( TDestDelegate ) . GetGenericArguments ( ) [ 0 ] ;
68-
69- var typeMappings = new Dictionary < Type , Type > ( )
70- . AddTypeMappingsFromDelegates ( typeSourceFunc , typeDestFunc ) ;
71-
72- XpressionMapperVisitor visitor = new MapIncludesVisitor ( mapper == null ? Mapper . Configuration : mapper . ConfigurationProvider , typeMappings ) ;
73- var remappedBody = visitor . Visit ( expression . Body ) ;
74- if ( remappedBody == null )
75- throw new InvalidOperationException ( Resource . cantRemapExpression ) ;
103+ return default ;
76104
77- return ( TDestDelegate ) Lambda ( typeDestFunc , remappedBody , expression . GetDestinationParameterExpressions ( visitor . InfoDictionary , typeMappings ) ) ;
105+ return mapper . MapExpression < TDestDelegate >
106+ (
107+ expression ,
108+ ( config , mappings ) => new MapIncludesVisitor ( mapper , config , mappings )
109+ ) ;
78110 }
79111
80112 /// <summary>
@@ -87,7 +119,7 @@ public static TDestDelegate MapExpressionAsInclude<TDestDelegate>(this IMapper m
87119 /// <returns></returns>
88120 public static TDestDelegate MapExpressionAsInclude < TSourceDelegate , TDestDelegate > ( this IMapper mapper , TSourceDelegate expression )
89121 where TSourceDelegate : LambdaExpression
90- where TDestDelegate : LambdaExpression
122+ where TDestDelegate : LambdaExpression
91123 => mapper . MapExpressionAsInclude < TDestDelegate > ( expression ) ;
92124
93125 /// <summary>
@@ -100,7 +132,7 @@ public static TDestDelegate MapExpressionAsInclude<TSourceDelegate, TDestDelegat
100132 /// <returns></returns>
101133 public static ICollection < TDestDelegate > MapExpressionList < TSourceDelegate , TDestDelegate > ( this IMapper mapper , ICollection < TSourceDelegate > collection )
102134 where TSourceDelegate : LambdaExpression
103- where TDestDelegate : LambdaExpression
135+ where TDestDelegate : LambdaExpression
104136 => collection ? . Select ( mapper . MapExpression < TSourceDelegate , TDestDelegate > ) . ToList ( ) ;
105137
106138 /// <summary>
@@ -111,7 +143,7 @@ public static ICollection<TDestDelegate> MapExpressionList<TSourceDelegate, TDes
111143 /// <param name="collection"></param>
112144 /// <returns></returns>
113145 public static ICollection < TDestDelegate > MapExpressionList < TDestDelegate > ( this IMapper mapper , IEnumerable < LambdaExpression > collection )
114- where TDestDelegate : LambdaExpression
146+ where TDestDelegate : LambdaExpression
115147 => collection ? . Select ( mapper . MapExpression < TDestDelegate > ) . ToList ( ) ;
116148
117149 /// <summary>
@@ -124,7 +156,7 @@ public static ICollection<TDestDelegate> MapExpressionList<TDestDelegate>(this I
124156 /// <returns></returns>
125157 public static ICollection < TDestDelegate > MapIncludesList < TSourceDelegate , TDestDelegate > ( this IMapper mapper , ICollection < TSourceDelegate > collection )
126158 where TSourceDelegate : LambdaExpression
127- where TDestDelegate : LambdaExpression
159+ where TDestDelegate : LambdaExpression
128160 => collection ? . Select ( mapper . MapExpressionAsInclude < TSourceDelegate , TDestDelegate > ) . ToList ( ) ;
129161
130162 /// <summary>
@@ -135,7 +167,7 @@ public static ICollection<TDestDelegate> MapIncludesList<TSourceDelegate, TDestD
135167 /// <param name="collection"></param>
136168 /// <returns></returns>
137169 public static ICollection < TDestDelegate > MapIncludesList < TDestDelegate > ( this IMapper mapper , IEnumerable < LambdaExpression > collection )
138- where TDestDelegate : LambdaExpression
170+ where TDestDelegate : LambdaExpression
139171 => collection ? . Select ( mapper . MapExpressionAsInclude < TDestDelegate > ) . ToList ( ) ;
140172
141173 /// <summary>
@@ -161,47 +193,37 @@ public static List<ParameterExpression> GetDestinationParameterExpressions(this
161193 /// <typeparam name="TSource"></typeparam>
162194 /// <typeparam name="TDest"></typeparam>
163195 /// <param name="typeMappings"></param>
196+ /// <param name="configurationProvider"></param>
164197 /// <returns></returns>
165- public static Dictionary < Type , Type > AddTypeMapping < TSource , TDest > ( this Dictionary < Type , Type > typeMappings )
198+ public static Dictionary < Type , Type > AddTypeMapping < TSource , TDest > ( this Dictionary < Type , Type > typeMappings , IConfigurationProvider configurationProvider )
166199 => typeMappings == null
167200 ? throw new ArgumentException ( Resource . typeMappingsDictionaryIsNull )
168- : typeMappings . AddTypeMapping ( typeof ( TSource ) , typeof ( TDest ) ) ;
201+ : typeMappings . AddTypeMapping ( configurationProvider , typeof ( TSource ) , typeof ( TDest ) ) ;
169202
170203 private static bool HasUnderlyingType ( this Type type )
171204 {
172205 return ( type . IsGenericType ( ) && typeof ( System . Collections . IEnumerable ) . IsAssignableFrom ( type ) ) || type . IsArray ;
173206 }
174207
175- private static void AddUnderlyingTypes ( this Dictionary < Type , Type > typeMappings , Type sourceType , Type destType )
208+ private static void AddUnderlyingTypes ( this Dictionary < Type , Type > typeMappings , IConfigurationProvider configurationProvider , Type sourceType , Type destType )
176209 {
177- var sourceArguments = ! sourceType . HasUnderlyingType ( )
178- ? new List < Type > ( )
179- : ElementTypeHelper . GetElementTypes ( sourceType ) . ToList ( ) ;
180-
181- var destArguments = ! destType . HasUnderlyingType ( )
182- ? new List < Type > ( )
183- : ElementTypeHelper . GetElementTypes ( destType ) . ToList ( ) ;
184-
185- if ( sourceArguments . Count != destArguments . Count )
186- throw new ArgumentException ( Resource . invalidArgumentCount ) ;
187-
188- sourceArguments . Aggregate ( typeMappings , ( dic , next ) =>
189- {
190- if ( ! dic . ContainsKey ( next ) && next != destArguments [ sourceArguments . IndexOf ( next ) ] )
191- dic . AddTypeMapping ( next , destArguments [ sourceArguments . IndexOf ( next ) ] ) ;
192-
193- return dic ;
194- } ) ;
210+ typeMappings . DoAddTypeMappings
211+ (
212+ configurationProvider ,
213+ ! sourceType . HasUnderlyingType ( ) ? new List < Type > ( ) : ElementTypeHelper . GetElementTypes ( sourceType ) . ToList ( ) ,
214+ ! destType . HasUnderlyingType ( ) ? new List < Type > ( ) : ElementTypeHelper . GetElementTypes ( destType ) . ToList ( )
215+ ) ;
195216 }
196217
197218 /// <summary>
198219 /// Adds a new source and destination key-value pair to a dictionary of type mappings based on the arguments.
199220 /// </summary>
200221 /// <param name="typeMappings"></param>
222+ /// <param name="configurationProvider"></param>
201223 /// <param name="sourceType"></param>
202224 /// <param name="destType"></param>
203225 /// <returns></returns>
204- public static Dictionary < Type , Type > AddTypeMapping ( this Dictionary < Type , Type > typeMappings , Type sourceType , Type destType )
226+ public static Dictionary < Type , Type > AddTypeMapping ( this Dictionary < Type , Type > typeMappings , IConfigurationProvider configurationProvider , Type sourceType , Type destType )
205227 {
206228 if ( typeMappings == null )
207229 throw new ArgumentException ( Resource . typeMappingsDictionaryIsNull ) ;
@@ -216,32 +238,75 @@ public static Dictionary<Type, Type> AddTypeMapping(this Dictionary<Type, Type>
216238 {
217239 typeMappings . Add ( sourceType , destType ) ;
218240 if ( typeof ( Delegate ) . IsAssignableFrom ( sourceType ) )
219- typeMappings . AddTypeMappingsFromDelegates ( sourceType , destType ) ;
241+ typeMappings . AddTypeMappingsFromDelegates ( configurationProvider , sourceType , destType ) ;
220242 else
221- typeMappings . AddUnderlyingTypes ( sourceType , destType ) ;
243+ {
244+ typeMappings . AddUnderlyingTypes ( configurationProvider , sourceType , destType ) ;
245+ typeMappings . FindChildPropertyTypeMaps ( configurationProvider , sourceType , destType ) ;
246+ }
222247 }
223248
224249 return typeMappings ;
225250 }
226251
227- private static Dictionary < Type , Type > AddTypeMappingsFromDelegates ( this Dictionary < Type , Type > typeMappings , Type sourceType , Type destType )
252+ private static Dictionary < Type , Type > AddTypeMappingsFromDelegates ( this Dictionary < Type , Type > typeMappings , IConfigurationProvider configurationProvider , Type sourceType , Type destType )
228253 {
229254 if ( typeMappings == null )
230255 throw new ArgumentException ( Resource . typeMappingsDictionaryIsNull ) ;
231256
232- var sourceArguments = sourceType . GetGenericArguments ( ) . ToList ( ) ;
233- var destArguments = destType . GetGenericArguments ( ) . ToList ( ) ;
257+ typeMappings . DoAddTypeMappings
258+ (
259+ configurationProvider ,
260+ sourceType . GetGenericArguments ( ) . ToList ( ) ,
261+ destType . GetGenericArguments ( ) . ToList ( )
262+ ) ;
234263
264+ return typeMappings ;
265+ }
266+
267+ private static void DoAddTypeMappings ( this Dictionary < Type , Type > typeMappings , IConfigurationProvider configurationProvider , List < Type > sourceArguments , List < Type > destArguments )
268+ {
235269 if ( sourceArguments . Count != destArguments . Count )
236270 throw new ArgumentException ( Resource . invalidArgumentCount ) ;
237271
238- return sourceArguments . Aggregate ( typeMappings , ( dic , next ) =>
272+ for ( int i = 0 ; i < sourceArguments . Count ; i ++ )
239273 {
240- if ( ! dic . ContainsKey ( next ) && next != destArguments [ sourceArguments . IndexOf ( next ) ] )
241- dic . AddTypeMapping ( next , destArguments [ sourceArguments . IndexOf ( next ) ] ) ;
274+ if ( ! typeMappings . ContainsKey ( sourceArguments [ i ] ) && sourceArguments [ i ] != destArguments [ i ] )
275+ typeMappings . AddTypeMapping ( configurationProvider , sourceArguments [ i ] , destArguments [ i ] ) ;
276+ }
277+ }
242278
243- return dic ;
244- } ) ;
279+ private static void FindChildPropertyTypeMaps ( this Dictionary < Type , Type > typeMappings , IConfigurationProvider ConfigurationProvider , Type source , Type dest )
280+ {
281+ //The destination becomes the source because to map a source expression to a destination expression,
282+ //we need the expressions used to create the source from the destination
283+ var typeMap = ConfigurationProvider . ResolveTypeMap ( sourceType : dest , destinationType : source ) ;
284+
285+ if ( typeMap == null )
286+ return ;
287+
288+ FindMaps ( typeMap . GetPropertyMaps ( ) . ToList ( ) ) ;
289+ void FindMaps ( List < PropertyMap > maps )
290+ {
291+ foreach ( PropertyMap pm in maps )
292+ {
293+ if ( pm . SourceMember == null )
294+ continue ;
295+
296+ AddChildMappings
297+ (
298+ source . GetFieldOrProperty ( pm . DestinationProperty . Name ) . GetMemberType ( ) ,
299+ pm . SourceMember . GetMemberType ( )
300+ ) ;
301+ void AddChildMappings ( Type sourcePropertyType , Type destPropertyType )
302+ {
303+ if ( sourcePropertyType . IsLiteralType ( ) || destPropertyType . IsLiteralType ( ) )
304+ return ;
305+
306+ typeMappings . AddTypeMapping ( ConfigurationProvider , sourcePropertyType , destPropertyType ) ;
307+ }
308+ }
309+ }
245310 }
246311 }
247312}
0 commit comments