@@ -312,6 +312,18 @@ internal static Action<TSource, TDestination, ISet<string>> BuildMap<TSource, TD
312312 }
313313 }
314314
315+ // Check if this is a complex object that needs deep mapping
316+ if ( IsComplexType ( srcType ) && IsComplexType ( dstType ) && srcType != dstType )
317+ {
318+ // Create deep mapping for complex objects
319+ var deepMapping = CreateDeepMapping < TSource , TDestination > ( dst . Name , src , dst , srcType , dstType ) ;
320+ if ( deepMapping . Getter != null && deepMapping . Setter != null )
321+ {
322+ mappings . Add ( deepMapping ) ;
323+ continue ;
324+ }
325+ }
326+
315327 // Normal property mapping
316328 var converter = CreateConverter ( srcType , dstType ) ;
317329 if ( converter == null )
@@ -859,6 +871,103 @@ private static Type GetElementType(Type collectionType)
859871
860872 return typeof ( object ) ;
861873 }
874+
875+ // Check if a type is a complex object that needs deep mapping
876+ private static bool IsComplexType ( Type type )
877+ {
878+ if ( type == null ) return false ;
879+
880+ // Handle nullable types
881+ Type underlyingType = Nullable . GetUnderlyingType ( type ) ?? type ;
882+
883+ // Skip primitive types, strings, and enums
884+ if ( underlyingType . IsPrimitive ||
885+ underlyingType == typeof ( string ) ||
886+ underlyingType == typeof ( DateTime ) ||
887+ underlyingType == typeof ( TimeSpan ) ||
888+ underlyingType == typeof ( DateTimeOffset ) ||
889+ underlyingType == typeof ( Guid ) ||
890+ underlyingType == typeof ( decimal ) ||
891+ underlyingType . IsEnum )
892+ {
893+ return false ;
894+ }
895+
896+ // Skip collections
897+ if ( IsCollection ( type ) )
898+ {
899+ return false ;
900+ }
901+
902+ // Check if it's a class with properties (complex object)
903+ return underlyingType . IsClass &&
904+ underlyingType != typeof ( object ) &&
905+ GetTypeProperties ( underlyingType ) . Length > 0 ;
906+ }
907+
908+ // Create deep mapping for complex nested objects
909+ private static MappingEntry < TSource , TDestination > CreateDeepMapping < TSource , TDestination > (
910+ string name , PropertyInfo srcProp , PropertyInfo dstProp , Type srcType , Type dstType )
911+ {
912+ // Create a getter that retrieves the source object
913+ var srcParam = Expression . Parameter ( typeof ( TSource ) , "s" ) ;
914+ var srcPropExpr = Expression . Property ( srcParam , srcProp ) ;
915+
916+ // Create the deep mapping method call
917+ var mapMethod = typeof ( ValueMapper ) . GetMethod ( nameof ( Map ) , new [ ] { srcType , typeof ( ISet < string > ) } )
918+ ?? typeof ( ValueMapper ) . GetMethods ( )
919+ . Where ( m => m . Name == nameof ( Map ) && m . GetGenericArguments ( ) . Length == 2 )
920+ . FirstOrDefault ( ) ? . MakeGenericMethod ( srcType , dstType ) ;
921+
922+ if ( mapMethod == null )
923+ {
924+ // Fallback: try to get the generic Map method and make it generic
925+ mapMethod = typeof ( ValueMapper ) . GetMethods ( BindingFlags . Public | BindingFlags . Static )
926+ . Where ( m => m . Name == nameof ( Map ) &&
927+ m . IsGenericMethodDefinition &&
928+ m . GetGenericArguments ( ) . Length == 2 &&
929+ m . GetParameters ( ) . Length == 2 )
930+ . FirstOrDefault ( ) ? . MakeGenericMethod ( srcType , dstType ) ;
931+ }
932+
933+ if ( mapMethod == null )
934+ {
935+ return new MappingEntry < TSource , TDestination > ( name , null , null , dstType , true ) ;
936+ }
937+
938+ // Create getter that performs deep mapping
939+ Func < TSource , object > getter = source =>
940+ {
941+ try
942+ {
943+ var sourceValue = srcProp . GetValue ( source ) ;
944+ if ( sourceValue == null )
945+ return null ;
946+
947+ // Use reflection to call the generic Map method
948+ return mapMethod . Invoke ( null , new object [ ] { sourceValue , null } ) ;
949+ }
950+ catch
951+ {
952+ return null ;
953+ }
954+ } ;
955+
956+ // Create setter for the destination property
957+ var dstParam = Expression . Parameter ( typeof ( TDestination ) , "d" ) ;
958+ var valueParam = Expression . Parameter ( typeof ( object ) , "v" ) ;
959+ var setterExpr = Expression . Lambda < Action < TDestination , object > > (
960+ Expression . Call (
961+ dstParam ,
962+ dstProp . GetSetMethod ( true ) ,
963+ Expression . Convert ( valueParam , dstType )
964+ ) ,
965+ dstParam , valueParam
966+ ) ;
967+ var setter = setterExpr . Compile ( ) ;
968+
969+ return new MappingEntry < TSource , TDestination > ( name , getter , setter , dstType , true ) ;
970+ }
862971 }
863972}
864973// Internal implementation detail - hidden from API
0 commit comments