1- using System ;
1+ using AutoMapper . Extensions . ExpressionMapping . Extensions ;
2+ using AutoMapper . Extensions . ExpressionMapping . Structures ;
3+ using AutoMapper . Internal ;
4+ using AutoMapper . QueryableExtensions . Impl ;
5+ using System ;
26using System . Collections . Generic ;
37using System . Globalization ;
48using System . Linq ;
59using System . Linq . Expressions ;
610using System . Reflection ;
711using System . Text ;
8- using AutoMapper . Extensions . ExpressionMapping . ArgumentMappers ;
9- using AutoMapper . Extensions . ExpressionMapping . Extensions ;
10- using AutoMapper . Extensions . ExpressionMapping . Structures ;
11- using AutoMapper . Internal ;
12- using AutoMapper . QueryableExtensions . Impl ;
1312
1413namespace AutoMapper . Extensions . ExpressionMapping
1514{
@@ -175,34 +174,119 @@ protected override Expression VisitMemberInit(MemberInitExpression node)
175174 //The destination becomes the source because to map a source expression to a destination expression,
176175 //we need the expressions used to create the source from the destination
177176
178- IEnumerable < MemberBinding > bindings = node . Bindings . Aggregate ( new List < MemberBinding > ( ) , ( list , binding ) =>
179- {
180- var propertyMap = typeMap . PropertyMaps . SingleOrDefault ( item => item . DestinationName == binding . Member . Name ) ;
181- if ( propertyMap == null )
182- return list ;
177+ return GetMemberInit
178+ (
179+ new MemberBindingGroup
180+ (
181+ declaringMemberKey : null ,
182+ isRootMemberAssignment : true ,
183+ newType : newType ,
184+ memberAssignmentInfos : node . Bindings . OfType < MemberAssignment > ( ) . Aggregate ( new List < MemberAssignmentInfo > ( ) , ( list , binding ) =>
185+ {
186+ var propertyMap = typeMap . PropertyMaps . SingleOrDefault ( item => item . DestinationName == binding . Member . Name ) ;
187+ if ( propertyMap == null )
188+ return list ;
189+
190+ list . Add ( new MemberAssignmentInfo ( propertyMap , binding ) ) ;
191+ return list ;
192+ } )
193+ )
194+ ) ;
195+ }
196+
197+ return base . VisitMemberInit ( node ) ;
198+
199+ }
183200
184- var sourceMember = GetSourceMember ( propertyMap ) ;
185- if ( sourceMember == null )
186- return list ;
201+ private MemberInitExpression GetMemberInit ( MemberBindingGroup memberBindingGroup )
202+ {
203+ Dictionary < DeclaringMemberKey , List < MemberAssignmentInfo > > includedMembers = new Dictionary < DeclaringMemberKey , List < MemberAssignmentInfo > > ( ) ;
204+
205+ List < MemberBinding > bindings = memberBindingGroup . MemberAssignmentInfos . Aggregate ( new List < MemberBinding > ( ) , ( list , next ) =>
206+ {
207+ var propertyMap = next . PropertyMap ;
208+ var binding = next . MemberAssignment ;
209+
210+ var sourceMember = GetSourceMember ( propertyMap ) ; //does the corresponding member mapping exist
211+ if ( sourceMember == null )
212+ return list ;
213+
214+ DeclaringMemberKey declaringMemberKey = new DeclaringMemberKey
215+ (
216+ GetParentMember ( propertyMap ) ,
217+ BuildParentFullName ( propertyMap )
218+ ) ;
187219
188- Expression bindingExpression = ( ( MemberAssignment ) binding ) . Expression ;
189- list . Add
220+ if ( ShouldBindPropertyMap ( next ) )
221+ {
222+ list . Add //adding bindings for property maps
190223 (
191224 DoBind
192225 (
193226 sourceMember ,
194- bindingExpression ,
195- this . Visit ( bindingExpression )
227+ binding . Expression ,
228+ this . Visit ( binding . Expression )
196229 )
197230 ) ;
231+ }
232+ else
233+ {
234+ if ( declaringMemberKey . DeclaringMemberInfo == null )
235+ throw new ArgumentNullException ( nameof ( declaringMemberKey . DeclaringMemberInfo ) ) ;
198236
199- return list ;
200- } ) ;
237+ if ( ! includedMembers . TryGetValue ( declaringMemberKey , out List < MemberAssignmentInfo > assignments ) )
238+ {
239+ includedMembers . Add
240+ (
241+ declaringMemberKey ,
242+ new List < MemberAssignmentInfo >
243+ {
244+ new MemberAssignmentInfo
245+ (
246+ propertyMap ,
247+ binding
248+ )
249+ }
250+ ) ;
251+ }
252+ else
253+ {
254+ assignments . Add ( new MemberAssignmentInfo ( propertyMap , binding ) ) ;
255+ }
256+ }
201257
202- return Expression . MemberInit ( Expression . New ( newType ) , bindings ) ;
203- }
258+ return list ;
204259
205- return base . VisitMemberInit ( node ) ;
260+ bool ShouldBindPropertyMap ( MemberAssignmentInfo memberAssignmentInfo )
261+ => ( memberBindingGroup . IsRootMemberAssignment && sourceMember . ReflectedType == memberBindingGroup . NewType )
262+ || ( ! memberBindingGroup . IsRootMemberAssignment && declaringMemberKey . Equals ( memberBindingGroup . DeclaringMemberKey ) ) ;
263+ } ) ;
264+
265+ includedMembers . Select
266+ (
267+ kvp => new MemberBindingGroup
268+ (
269+ declaringMemberKey : kvp . Key ,
270+ isRootMemberAssignment : false ,
271+ newType : kvp . Key . DeclaringMemberInfo . GetMemberType ( ) ,
272+ memberAssignmentInfos : includedMembers . Values . SelectMany ( m => m ) . ToList ( )
273+ )
274+ )
275+ . ToList ( )
276+ . ForEach ( group =>
277+ {
278+ if ( ShouldBindChildReference ( group ) )
279+ bindings . Add ( Expression . Bind ( group . DeclaringMemberKey . DeclaringMemberInfo , GetMemberInit ( group ) ) ) ;
280+ } ) ;
281+
282+ bool ShouldBindChildReference ( MemberBindingGroup group )
283+ => ( memberBindingGroup . IsRootMemberAssignment
284+ && group . DeclaringMemberKey . DeclaringMemberInfo . ReflectedType == memberBindingGroup . NewType )
285+ || ( ! memberBindingGroup . IsRootMemberAssignment
286+ && group . DeclaringMemberKey . DeclaringMemberInfo . ReflectedType == memberBindingGroup . NewType
287+ && group . DeclaringMemberKey . DeclaringMemberFullName . StartsWith ( memberBindingGroup . DeclaringMemberKey . DeclaringMemberFullName ) ) ;
288+
289+ return Expression . MemberInit ( Expression . New ( memberBindingGroup . NewType ) , bindings ) ;
206290 }
207291
208292 private MemberBinding DoBind ( MemberInfo sourceMember , Expression initial , Expression mapped )
@@ -217,6 +301,39 @@ private MemberInfo GetSourceMember(PropertyMap propertyMap)
217301 ? propertyMap . CustomMapExpression . GetMemberExpression ( ) ? . Member
218302 : propertyMap . SourceMember ;
219303
304+ private MemberInfo GetParentMember ( PropertyMap propertyMap )
305+ => propertyMap . IncludedMember != null
306+ ? propertyMap . ProjectToCustomSource . GetMemberExpression ( ) . Member
307+ : GetSourceParentMember ( propertyMap ) ;
308+
309+ private MemberInfo GetSourceParentMember ( PropertyMap propertyMap )
310+ {
311+ if ( propertyMap . CustomMapExpression != null )
312+ return propertyMap . CustomMapExpression . GetMemberExpression ( ) ? . Expression . GetMemberExpression ( ) ? . Member ;
313+
314+ if ( propertyMap . SourceMembers . Count > 1 )
315+ return new List < MemberInfo > ( propertyMap . SourceMembers ) [ propertyMap . SourceMembers . Count - 2 ] ;
316+
317+ return null ;
318+ }
319+
320+ private string BuildParentFullName ( PropertyMap propertyMap )
321+ {
322+ List < PropertyMapInfo > propertyMapInfos = new List < PropertyMapInfo > ( ) ;
323+ if ( propertyMap . IncludedMember != null )
324+ propertyMapInfos . Add ( new PropertyMapInfo ( propertyMap . ProjectToCustomSource , new List < MemberInfo > ( ) ) ) ;
325+
326+ propertyMapInfos . Add ( new PropertyMapInfo ( propertyMap . CustomMapExpression , propertyMap . SourceMembers . ToList ( ) ) ) ;
327+
328+ List < string > fullNameArray = BuildFullName ( propertyMapInfos )
329+ . Split ( new char [ ] { '.' } )
330+ . ToList ( ) ;
331+
332+ fullNameArray . Remove ( fullNameArray . Last ( ) ) ;
333+
334+ return string . Join ( "." , fullNameArray ) ;
335+ }
336+
220337 protected override Expression VisitBinary ( BinaryExpression node )
221338 {
222339 return DoVisitBinary ( this . Visit ( node . Left ) , this . Visit ( node . Right ) , this . Visit ( node . Conversion ) ) ;
@@ -509,6 +626,9 @@ void CompareSourceAndDestLiterals(Type mappedPropertyType, string mappedProperty
509626 throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Resource . expressionMapValueTypeMustMatchFormat , mappedPropertyType . Name , mappedPropertyDescription , sourceMemberType . Name , propertyMap . DestinationMember . Name ) ) ;
510627 }
511628
629+ if ( propertyMap . ProjectToCustomSource != null )
630+ propertyMapInfoList . Add ( new PropertyMapInfo ( propertyMap . ProjectToCustomSource , new List < MemberInfo > ( ) ) ) ;
631+
512632 propertyMapInfoList . Add ( new PropertyMapInfo ( propertyMap . CustomMapExpression , propertyMap . SourceMembers . ToList ( ) ) ) ;
513633 }
514634 else
@@ -520,6 +640,9 @@ void CompareSourceAndDestLiterals(Type mappedPropertyType, string mappedProperty
520640 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.
521641 throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Resource . srcMemberCannotBeNullFormat , typeSource . Name , typeDestination . Name , propertyName ) ) ;
522642
643+ if ( propertyMap . ProjectToCustomSource != null )
644+ propertyMapInfoList . Add ( new PropertyMapInfo ( propertyMap . ProjectToCustomSource , new List < MemberInfo > ( ) ) ) ;
645+
523646 propertyMapInfoList . Add ( new PropertyMapInfo ( propertyMap . CustomMapExpression , propertyMap . SourceMembers . ToList ( ) ) ) ;
524647 var childFullName = sourceFullName . Substring ( sourceFullName . IndexOf ( period , StringComparison . OrdinalIgnoreCase ) + 1 ) ;
525648
0 commit comments