2
2
using System ;
3
3
using System . Collections ;
4
4
using System . Collections . Generic ;
5
+ using System . Diagnostics . CodeAnalysis ;
5
6
using System . Linq ;
7
+ using System . Linq . Expressions ;
6
8
using System . Reflection ;
7
9
8
10
namespace Mapster . Utils
@@ -19,32 +21,112 @@ public static Expression Assign(Expression left, Expression right)
19
21
20
22
public static Expression PropertyOrFieldPath ( Expression expr , string path )
21
23
{
22
- var props = path . Split ( '.' ) ;
23
- return props . Aggregate ( expr , PropertyOrField ) ;
24
+ Expression current = expr ;
25
+ string [ ] props = path . Split ( '.' ) ;
26
+
27
+ for ( int i = 0 ; i < props . Length ; i ++ )
28
+ {
29
+ if ( IsDictionaryKey ( current , props [ i ] , out Expression ? next ) )
30
+ {
31
+ current = next ;
32
+ continue ;
33
+ }
34
+
35
+ if ( IsPropertyOrField ( current , props [ i ] , out next ) )
36
+ {
37
+ current = next ;
38
+ continue ;
39
+ }
40
+
41
+ // For dynamically built types, it is possible to have periods in the property name.
42
+ // Rejoin an incrementing number of parts with periods to try and find a property match.
43
+ if ( IsPropertyOrFieldPathWithPeriods ( current , props [ i ..] , out next , out int combinationLength ) )
44
+ {
45
+ current = next ;
46
+ i += combinationLength - 1 ;
47
+ continue ;
48
+ }
49
+
50
+ throw new ArgumentException ( $ "'{ props [ i ] } ' is not a member of type '{ current . Type . FullName } '", nameof ( path ) ) ;
51
+ }
52
+
53
+ return current ;
24
54
}
25
55
26
- private static Expression PropertyOrField ( Expression expr , string prop )
56
+ private static bool IsPropertyOrFieldPathWithPeriods ( Expression expr , string [ ] path , [ NotNullWhen ( true ) ] out Expression ? propExpr , out int combinationLength )
57
+ {
58
+ if ( path . Length < 2 )
59
+ {
60
+ propExpr = null ;
61
+ combinationLength = 0 ;
62
+ return false ;
63
+ }
64
+
65
+ for ( int count = 2 ; count <= path . Length ; count ++ )
66
+ {
67
+ string prop = string . Join ( '.' , path [ ..count ] ) ;
68
+ if ( IsPropertyOrField ( expr , prop , out propExpr ) )
69
+ {
70
+ combinationLength = count ;
71
+ return true ;
72
+ }
73
+ }
74
+
75
+ propExpr = null ;
76
+ combinationLength = 0 ;
77
+ return false ;
78
+ }
79
+
80
+ private static bool IsDictionaryKey ( Expression expr , string prop , [ NotNullWhen ( true ) ] out Expression ? propExpr )
27
81
{
28
82
var type = expr . Type ;
29
83
var dictType = type . GetDictionaryType ( ) ;
30
- if ( dictType ? . GetGenericArguments ( ) [ 0 ] == typeof ( string ) )
84
+
85
+ if ( dictType ? . GetGenericArguments ( ) [ 0 ] != typeof ( string ) )
31
86
{
87
+ propExpr = null ;
88
+ return false ;
89
+ }
90
+
32
91
var method = typeof ( MapsterHelper ) . GetMethods ( )
33
92
. First ( m => m . Name == nameof ( MapsterHelper . GetValueOrDefault ) && m . GetParameters ( ) [ 0 ] . ParameterType . Name == dictType . Name )
34
93
. MakeGenericMethod ( dictType . GetGenericArguments ( ) ) ;
35
94
36
- return Expression . Call ( method , expr . To ( type ) , Expression . Constant ( prop ) ) ;
95
+ propExpr = Expression . Call ( method , expr . To ( type ) , Expression . Constant ( prop ) ) ;
96
+ return true ;
37
97
}
38
98
99
+ private static bool IsPropertyOrField ( Expression expr , string prop , [ NotNullWhen ( true ) ] out Expression ? propExpr )
100
+ {
101
+ Type type = expr . Type ;
102
+
39
103
if ( type . GetTypeInfo ( ) . IsInterface )
40
104
{
41
105
var allTypes = type . GetAllInterfaces ( ) ;
42
106
var flags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ;
43
107
var interfaceType = allTypes . FirstOrDefault ( it => it . GetProperty ( prop , flags ) != null || it . GetField ( prop , flags ) != null ) ;
44
108
if ( interfaceType != null )
109
+ {
45
110
expr = Expression . Convert ( expr , interfaceType ) ;
111
+ type = expr . Type ;
112
+ }
46
113
}
47
- return Expression . PropertyOrField ( expr , prop ) ;
114
+
115
+ MemberInfo ? propertyOrField = type
116
+ . GetMember (
117
+ prop ,
118
+ MemberTypes . Field | MemberTypes . Property ,
119
+ BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance | BindingFlags . FlattenHierarchy )
120
+ . FirstOrDefault ( ) ;
121
+
122
+ propExpr = propertyOrField ? . MemberType switch
123
+ {
124
+ MemberTypes . Property => Expression . Property ( expr , ( PropertyInfo ) propertyOrField ) ,
125
+ MemberTypes . Field => Expression . Field ( expr , ( FieldInfo ) propertyOrField ) ,
126
+ _ => null
127
+ } ;
128
+
129
+ return propExpr != null ;
48
130
}
49
131
50
132
private static bool IsReferenceAssignableFrom ( this Type destType , Type srcType )
0 commit comments