@@ -86,36 +86,7 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
86
86
// {1}
87
87
var right = Expression . Constant ( convertedValue , property . PropertyType ) ;
88
88
89
- Expression body ;
90
- switch ( filterQuery . FilterOperation )
91
- {
92
- case FilterOperations . eq :
93
- // {model.Id == 1}
94
- body = Expression . Equal ( left , right ) ;
95
- break ;
96
- case FilterOperations . lt :
97
- // {model.Id < 1}
98
- body = Expression . LessThan ( left , right ) ;
99
- break ;
100
- case FilterOperations . gt :
101
- // {model.Id > 1}
102
- body = Expression . GreaterThan ( left , right ) ;
103
- break ;
104
- case FilterOperations . le :
105
- // {model.Id <= 1}
106
- body = Expression . LessThanOrEqual ( left , right ) ;
107
- break ;
108
- case FilterOperations . ge :
109
- // {model.Id <= 1}
110
- body = Expression . GreaterThanOrEqual ( left , right ) ;
111
- break ;
112
- case FilterOperations . like :
113
- // {model.Id <= 1}
114
- body = Expression . Call ( left , "Contains" , null , right ) ;
115
- break ;
116
- default :
117
- throw new JsonApiException ( "500" , $ "Unknown filter operation { filterQuery . FilterOperation } ") ;
118
- }
89
+ var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
119
90
120
91
var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
121
92
@@ -126,27 +97,109 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
126
97
throw new JsonApiException ( "400" , $ "Could not cast { filterQuery . PropertyValue } to { property . PropertyType . Name } ") ;
127
98
}
128
99
}
100
+
101
+ public static IQueryable < TSource > Filter < TSource > ( this IQueryable < TSource > source , RelatedAttrFilterQuery filterQuery )
102
+ {
103
+ if ( filterQuery == null )
104
+ return source ;
105
+
106
+ var concreteType = typeof ( TSource ) ;
107
+ var relation = concreteType . GetProperty ( filterQuery . FilteredRelationship . InternalRelationshipName ) ;
108
+ if ( relation == null )
109
+ throw new ArgumentException ( $ "'{ filterQuery . FilteredRelationship . InternalRelationshipName } ' is not a valid relationship of '{ concreteType } '") ;
110
+
111
+ var relatedType = filterQuery . FilteredRelationship . Type ;
112
+ var relatedAttr = relatedType . GetProperty ( filterQuery . FilteredAttribute . InternalAttributeName ) ;
113
+ if ( relatedAttr == null )
114
+ throw new ArgumentException ( $ "'{ filterQuery . FilteredAttribute . InternalAttributeName } ' is not a valid attribute of '{ filterQuery . FilteredRelationship . InternalRelationshipName } '") ;
115
+
116
+ try
117
+ {
118
+ // convert the incoming value to the target value type
119
+ // "1" -> 1
120
+ var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , relatedAttr . PropertyType ) ;
121
+ // {model}
122
+ var parameter = Expression . Parameter ( concreteType , "model" ) ;
123
+
124
+ // {model.Relationship}
125
+ var leftRelationship = Expression . PropertyOrField ( parameter , relation . Name ) ;
126
+
127
+ // {model.Relationship.Attr}
128
+ var left = Expression . PropertyOrField ( leftRelationship , relatedAttr . Name ) ;
129
+
130
+ // {1}
131
+ var right = Expression . Constant ( convertedValue , relatedAttr . PropertyType ) ;
132
+
133
+ var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
134
+
135
+ var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
136
+
137
+ return source . Where ( lambda ) ;
138
+ }
139
+ catch ( FormatException )
140
+ {
141
+ throw new JsonApiException ( "400" , $ "Could not cast { filterQuery . PropertyValue } to { relatedAttr . PropertyType . Name } ") ;
142
+ }
143
+ }
144
+
145
+ private static Expression GetFilterExpressionLambda ( Expression left , Expression right , FilterOperations operation )
146
+ {
147
+ Expression body ;
148
+ switch ( operation )
149
+ {
150
+ case FilterOperations . eq :
151
+ // {model.Id == 1}
152
+ body = Expression . Equal ( left , right ) ;
153
+ break ;
154
+ case FilterOperations . lt :
155
+ // {model.Id < 1}
156
+ body = Expression . LessThan ( left , right ) ;
157
+ break ;
158
+ case FilterOperations . gt :
159
+ // {model.Id > 1}
160
+ body = Expression . GreaterThan ( left , right ) ;
161
+ break ;
162
+ case FilterOperations . le :
163
+ // {model.Id <= 1}
164
+ body = Expression . LessThanOrEqual ( left , right ) ;
165
+ break ;
166
+ case FilterOperations . ge :
167
+ // {model.Id <= 1}
168
+ body = Expression . GreaterThanOrEqual ( left , right ) ;
169
+ break ;
170
+ case FilterOperations . like :
171
+ // {model.Id <= 1}
172
+ body = Expression . Call ( left , "Contains" , null , right ) ;
173
+ break ;
174
+ default :
175
+ throw new JsonApiException ( "500" , $ "Unknown filter operation { operation } ") ;
176
+ }
177
+
178
+ return body ;
179
+ }
180
+
181
+
129
182
public static IQueryable < TSource > Select < TSource > ( this IQueryable < TSource > source , IEnumerable < string > columns )
130
183
{
131
- if ( columns == null || columns . Count ( ) == 0 )
184
+ if ( columns == null || columns . Count ( ) == 0 )
132
185
return source ;
133
186
134
187
var sourceType = source . ElementType ;
135
-
188
+
136
189
var resultType = typeof ( TSource ) ;
137
190
138
191
// {model}
139
192
var parameter = Expression . Parameter ( sourceType , "model" ) ;
140
-
193
+
141
194
var bindings = columns . Select ( column => Expression . Bind (
142
195
resultType . GetProperty ( column ) , Expression . PropertyOrField ( parameter , column ) ) ) ;
143
-
196
+
144
197
// { new Model () { Property = model.Property } }
145
198
var body = Expression . MemberInit ( Expression . New ( resultType ) , bindings ) ;
146
-
199
+
147
200
// { model => new TodoItem() { Property = model.Property } }
148
201
var selector = Expression . Lambda ( body , parameter ) ;
149
-
202
+
150
203
return source . Provider . CreateQuery < TSource > (
151
204
Expression . Call ( typeof ( Queryable ) , "Select" , new Type [ ] { sourceType , resultType } ,
152
205
source . Expression , Expression . Quote ( selector ) ) ) ;
0 commit comments