@@ -8,7 +8,9 @@ namespace QueryKit;
8
8
public class QueryKitPropertyMappings
9
9
{
10
10
private readonly Dictionary < string , QueryKitPropertyInfo > _propertyMappings = new ( ) ;
11
+ private readonly Dictionary < string , QueryKitPropertyInfo > _derivedPropertyMappings = new ( ) ;
11
12
internal IReadOnlyDictionary < string , QueryKitPropertyInfo > PropertyMappings => _propertyMappings ;
13
+ internal IReadOnlyDictionary < string , QueryKitPropertyInfo > DerivedPropertyMappings => _derivedPropertyMappings ;
12
14
13
15
public QueryKitPropertyMapping < TModel > Property < TModel > ( Expression < Func < TModel , object > > ? propertySelector )
14
16
{
@@ -25,7 +27,32 @@ public QueryKitPropertyMapping<TModel> Property<TModel>(Expression<Func<TModel,
25
27
26
28
return new QueryKitPropertyMapping < TModel > ( propertyInfo ) ;
27
29
}
28
-
30
+
31
+ public QueryKitPropertyMapping < TModel > DerivedProperty < TModel > ( Expression < Func < TModel , object > > ? propertySelector )
32
+ {
33
+ var fullPath = GetFullPropertyPath ( propertySelector ) ;
34
+
35
+ if ( propertySelector == null )
36
+ throw new ArgumentNullException ( nameof ( propertySelector ) ) ;
37
+ if ( propertySelector . NodeType != ExpressionType . Lambda )
38
+ throw new ArgumentException ( "Property selector must be a lambda expression" , nameof ( propertySelector ) ) ;
39
+
40
+ var body = propertySelector . Body ;
41
+
42
+ var propertyInfo = new QueryKitPropertyInfo
43
+ {
44
+ Name = fullPath ,
45
+ CanFilter = true ,
46
+ CanSort = true ,
47
+ QueryName = fullPath ,
48
+ DerivedExpression = body
49
+ } ;
50
+
51
+ _derivedPropertyMappings [ fullPath ] = propertyInfo ;
52
+
53
+ return new QueryKitPropertyMapping < TModel > ( propertyInfo ) ;
54
+ }
55
+
29
56
public string ReplaceAliasesWithPropertyPaths ( string input )
30
57
{
31
58
var operators = ComparisonOperator . List . Select ( x => x . Operator ( ) ) . ToList ( ) ;
@@ -42,56 +69,116 @@ public string ReplaceAliasesWithPropertyPaths(string input)
42
69
}
43
70
}
44
71
}
45
-
72
+
73
+ // foreach (var alias in _derivedPropertyMappings.Values)
74
+ // {
75
+ // foreach (var op in operators)
76
+ // {
77
+ // // Use regular expression to isolate left side of the expression
78
+ // var regex = new Regex($@"\b{alias.QueryName}\b(?=\s*{op})", RegexOptions.IgnoreCase);
79
+ // input = regex.Replace(input, $"~||~||~{alias.QueryName}");
80
+ // }
81
+ // }
46
82
return input ;
47
83
}
48
84
49
85
private static string GetFullPropertyPath ( Expression ? expression )
50
86
{
51
- if ( expression ! . NodeType == ExpressionType . Call )
52
- {
53
- var call = ( MethodCallExpression ) expression ;
54
- if ( call . Method . DeclaringType == typeof ( Enumerable ) && call . Method . Name == "Select" ||
55
- call . Method . DeclaringType == typeof ( Queryable ) && call . Method . Name == "Select" ||
56
- call . Method . DeclaringType == typeof ( Enumerable ) && call . Method . Name == "SelectMany" ||
57
- call . Method . DeclaringType == typeof ( Queryable ) && call . Method . Name == "SelectMany" )
58
- {
59
- var propertyPath = GetFullPropertyPath ( call . Arguments [ 1 ] ) ;
60
- var prevPath = GetFullPropertyPath ( call . Arguments [ 0 ] ) ;
61
- return $ "{ prevPath } .{ propertyPath } ";
62
- }
63
- }
87
+ if ( expression == null )
88
+ throw new ArgumentNullException ( nameof ( expression ) ) ;
64
89
65
- if ( expression ! . NodeType == ExpressionType . Lambda )
66
- {
67
- var lambda = ( LambdaExpression ) expression ;
68
- return GetFullPropertyPath ( lambda . Body ) ;
69
- }
70
- if ( expression . NodeType == ExpressionType . Convert )
71
- {
72
- var unary = ( UnaryExpression ) expression ;
73
- return GetFullPropertyPath ( unary . Operand ) ;
74
- }
75
- if ( expression . NodeType == ExpressionType . MemberAccess )
90
+ switch ( expression . NodeType )
76
91
{
77
- var memberExpression = ( MemberExpression ) expression ;
78
- return memberExpression ? . Expression ? . NodeType == ExpressionType . Parameter
79
- ? memberExpression . Member . Name
80
- : $ "{ GetFullPropertyPath ( memberExpression ? . Expression ) } .{ memberExpression ? . Member ? . Name } ";
92
+ case ExpressionType . Call :
93
+ var call = ( MethodCallExpression ) expression ;
94
+ if ( call . Method . DeclaringType == typeof ( Enumerable ) && call . Method . Name == "Select" ||
95
+ call . Method . DeclaringType == typeof ( Queryable ) && call . Method . Name == "Select" ||
96
+ call . Method . DeclaringType == typeof ( Enumerable ) && call . Method . Name == "SelectMany" ||
97
+ call . Method . DeclaringType == typeof ( Queryable ) && call . Method . Name == "SelectMany" )
98
+ {
99
+ var propertyPath = GetFullPropertyPath ( call . Arguments [ 1 ] ) ;
100
+ var prevPath = GetFullPropertyPath ( call . Arguments [ 0 ] ) ;
101
+ return $ "{ prevPath } .{ propertyPath } ";
102
+ }
103
+ break ;
104
+ case ExpressionType . Lambda :
105
+ var lambda = ( LambdaExpression ) expression ;
106
+ return GetFullPropertyPath ( lambda . Body ) ;
107
+ case ExpressionType . Convert :
108
+ var unary = ( UnaryExpression ) expression ;
109
+ return GetFullPropertyPath ( unary . Operand ) ;
110
+ case ExpressionType . MemberAccess :
111
+ var memberExpression = ( MemberExpression ) expression ;
112
+ return memberExpression ? . Expression ? . NodeType == ExpressionType . Parameter
113
+ ? memberExpression . Member . Name
114
+ : $ "{ GetFullPropertyPath ( memberExpression ? . Expression ) } .{ memberExpression ? . Member ? . Name } ";
115
+ case ExpressionType . Add :
116
+ case ExpressionType . Subtract :
117
+ case ExpressionType . Multiply :
118
+ case ExpressionType . Divide :
119
+ case ExpressionType . Modulo :
120
+ case ExpressionType . And :
121
+ case ExpressionType . Or :
122
+ case ExpressionType . AndAlso :
123
+ case ExpressionType . OrElse :
124
+ case ExpressionType . GreaterThan :
125
+ case ExpressionType . LessThan :
126
+ case ExpressionType . GreaterThanOrEqual :
127
+ case ExpressionType . LessThanOrEqual :
128
+ case ExpressionType . Equal :
129
+ var binary = ( BinaryExpression ) expression ;
130
+ var left = GetFullPropertyPath ( binary . Left ) ;
131
+ var right = GetFullPropertyPath ( binary . Right ) ;
132
+ var op = GetOperator ( binary . NodeType ) ;
133
+ return $ "{ left } { op } { right } ";
134
+ case ExpressionType . Constant :
135
+ var constant = ( ConstantExpression ) expression ;
136
+ return constant . Value ? . ToString ( ) ?? "" ;
137
+ default :
138
+ throw new NotSupportedException ( $ "Expression type '{ expression . NodeType } ' is not supported.") ;
81
139
}
140
+
82
141
throw new NotSupportedException ( $ "Expression type '{ expression . NodeType } ' is not supported.") ;
83
142
}
84
143
144
+ private static string GetOperator ( ExpressionType nodeType )
145
+ {
146
+ return nodeType switch
147
+ {
148
+ ExpressionType . Add => "+" ,
149
+ ExpressionType . Subtract => "-" ,
150
+ ExpressionType . Multiply => "*" ,
151
+ ExpressionType . Divide => "/" ,
152
+ ExpressionType . Modulo => "%" ,
153
+ ExpressionType . And => "&" ,
154
+ ExpressionType . Or => "|" ,
155
+ ExpressionType . AndAlso => "&&" ,
156
+ ExpressionType . OrElse => "||" ,
157
+ ExpressionType . GreaterThan => ">" ,
158
+ ExpressionType . LessThan => "<" ,
159
+ ExpressionType . GreaterThanOrEqual => ">=" ,
160
+ ExpressionType . LessThanOrEqual => "<=" ,
161
+ ExpressionType . Equal => "==" ,
162
+ _ => throw new NotSupportedException ( $ "Operator for expression type '{ nodeType } ' is not supported.")
163
+ } ;
164
+ }
165
+
166
+
167
+
85
168
public QueryKitPropertyInfo ? GetPropertyInfo ( string ? propertyName )
86
169
=> _propertyMappings . TryGetValue ( propertyName , out var info ) ? info : null ;
87
170
88
171
public QueryKitPropertyInfo ? GetPropertyInfoByQueryName ( string ? queryName )
89
172
=> _propertyMappings . Values . FirstOrDefault ( info => info . QueryName != null && info . QueryName . Equals ( queryName , StringComparison . InvariantCultureIgnoreCase ) ) ;
90
173
174
+ public QueryKitPropertyInfo ? GetDerivedPropertyInfoByQueryName ( string ? queryName )
175
+ => _derivedPropertyMappings . Values . FirstOrDefault ( info => info . QueryName != null && info . QueryName . Equals ( queryName , StringComparison . InvariantCultureIgnoreCase ) ) ;
176
+
91
177
public string ? GetPropertyPathByQueryName ( string ? queryName )
92
178
=> GetPropertyInfoByQueryName ( queryName ) ? . Name ?? null ;
93
179
}
94
180
181
+
95
182
public class QueryKitPropertyMapping < TModel >
96
183
{
97
184
private readonly QueryKitPropertyInfo _propertyInfo ;
@@ -126,4 +213,5 @@ public class QueryKitPropertyInfo
126
213
public bool CanFilter { get ; set ; }
127
214
public bool CanSort { get ; set ; }
128
215
public string ? QueryName { get ; set ; }
216
+ internal Expression DerivedExpression { get ; set ; }
129
217
}
0 commit comments