@@ -81,70 +81,17 @@ public static IOrderedQueryable<TSource> Sort<TSource>(this IOrderedQueryable<TS
8181 }
8282 }
8383
84- public static IOrderedQueryable < TSource > OrderBy < TSource > ( this IQueryable < TSource > source , AttrQuery attrQuery )
85- {
86- return CallGenericOrderMethod ( source , attrQuery . Attribute , null , "OrderBy" ) ;
87- }
88- public static IOrderedQueryable < TSource > OrderBy < TSource > ( this IQueryable < TSource > source , RelatedAttrQuery relatedAttrQuery )
89- {
90- return CallGenericOrderMethod ( source , relatedAttrQuery . Attribute , relatedAttrQuery . RelationshipAttribute , "OrderBy" ) ;
91- }
92-
93- public static IOrderedQueryable < TSource > OrderByDescending < TSource > ( this IQueryable < TSource > source , AttrQuery attrQuery )
94- {
95- return CallGenericOrderMethod ( source , attrQuery . Attribute , null , "OrderByDescending" ) ;
96- }
97- public static IOrderedQueryable < TSource > OrderByDescending < TSource > ( this IQueryable < TSource > source , RelatedAttrQuery relatedAttrQuery )
98- {
99- return CallGenericOrderMethod ( source , relatedAttrQuery . Attribute , relatedAttrQuery . RelationshipAttribute , "OrderByDescending" ) ;
100- }
84+ public static IOrderedQueryable < TSource > OrderBy < TSource > ( this IQueryable < TSource > source , BaseAttrQuery baseAttrQuery )
85+ => CallGenericOrderMethod ( source , baseAttrQuery , "OrderBy" ) ;
10186
102- public static IOrderedQueryable < TSource > ThenBy < TSource > ( this IOrderedQueryable < TSource > source , AttrQuery attrQuery )
103- {
104- return CallGenericOrderMethod ( source , attrQuery . Attribute , null , "ThenBy" ) ;
105- }
106- public static IOrderedQueryable < TSource > ThenBy < TSource > ( this IOrderedQueryable < TSource > source , RelatedAttrQuery relatedAttrQuery )
107- {
108- return CallGenericOrderMethod ( source , relatedAttrQuery . Attribute , relatedAttrQuery . RelationshipAttribute , "ThenBy" ) ;
109- }
87+ public static IOrderedQueryable < TSource > OrderByDescending < TSource > ( this IQueryable < TSource > source , BaseAttrQuery baseAttrQuery )
88+ => CallGenericOrderMethod ( source , baseAttrQuery , "OrderByDescending" ) ;
11089
111- public static IOrderedQueryable < TSource > ThenByDescending < TSource > ( this IOrderedQueryable < TSource > source , AttrQuery attrQuery )
112- {
113- return CallGenericOrderMethod ( source , attrQuery . Attribute , null , "ThenByDescending" ) ;
114- }
115- public static IOrderedQueryable < TSource > ThenByDescending < TSource > ( this IOrderedQueryable < TSource > source , RelatedAttrQuery relatedAttrQuery )
116- {
117- return CallGenericOrderMethod ( source , relatedAttrQuery . Attribute , relatedAttrQuery . RelationshipAttribute , "ThenByDescending" ) ;
118- }
90+ public static IOrderedQueryable < TSource > ThenBy < TSource > ( this IOrderedQueryable < TSource > source , BaseAttrQuery baseAttrQuery )
91+ => CallGenericOrderMethod ( source , baseAttrQuery , "ThenBy" ) ;
11992
120- private static IOrderedQueryable < TSource > CallGenericOrderMethod < TSource > ( IQueryable < TSource > source , AttrAttribute attr , RelationshipAttribute relationAttr , string method )
121- {
122- // {x}
123- var parameter = Expression . Parameter ( typeof ( TSource ) , "x" ) ;
124-
125- //var property = Expression.Property(parameter, attr.InternalAttributeName);
126-
127- MemberExpression member ;
128- // {x.relationship.propertyName}
129- if ( relationAttr != null )
130- {
131- var relation = Expression . PropertyOrField ( parameter , relationAttr . InternalRelationshipName ) ;
132- member = Expression . Property ( relation , attr . InternalAttributeName ) ;
133- }
134- // {x.propertyName}
135- else
136- member = Expression . Property ( parameter , attr . InternalAttributeName ) ;
137-
138- // {x=>x.propertyName} or {x=>x.relationship.propertyName}
139- var lambda = Expression . Lambda ( member , parameter ) ;
140-
141- // REFLECTION: source.OrderBy(x => x.Property)
142- var orderByMethod = typeof ( Queryable ) . GetMethods ( ) . First ( x => x . Name == method && x . GetParameters ( ) . Length == 2 ) ;
143- var orderByGeneric = orderByMethod . MakeGenericMethod ( typeof ( TSource ) , member . Type ) ;
144- var result = orderByGeneric . Invoke ( null , new object [ ] { source , lambda } ) ;
145-
146- return ( IOrderedQueryable < TSource > ) result ;
147- }
93+ public static IOrderedQueryable < TSource > ThenByDescending < TSource > ( this IOrderedQueryable < TSource > source , BaseAttrQuery baseAttrQuery )
94+ => CallGenericOrderMethod ( source , baseAttrQuery , "ThenByDescending" ) ;
14895
14996 public static IQueryable < TSource > Filter < TSource > ( this IQueryable < TSource > source , IJsonApiContext jsonApiContext , FilterQuery filterQuery )
15097 {
@@ -157,120 +104,17 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
157104 return source . Filter ( new AttrQuery ( jsonApiContext , filterQuery ) ) ;
158105 }
159106
160- public static IQueryable < TSource > Filter < TSource > ( this IQueryable < TSource > source , AttrQuery filterQuery )
107+ public static IQueryable < TSource > Filter < TSource > ( this IQueryable < TSource > source , BaseAttrQuery filterQuery )
161108 {
162109 if ( filterQuery == null )
163110 return source ;
164111
165- var concreteType = typeof ( TSource ) ;
166- var property = concreteType . GetProperty ( filterQuery . Attribute . InternalAttributeName ) ;
167- var op = filterQuery . FilterOperation ;
168-
169- if ( property == null )
170- throw new ArgumentException ( $ "'{ filterQuery . Attribute . InternalAttributeName } ' is not a valid property of '{ concreteType } '") ;
171-
172- try
173- {
174- if ( op == FilterOperations . @in || op == FilterOperations . nin )
175- {
176- string [ ] propertyValues = filterQuery . PropertyValue . Split ( ',' ) ;
177- var lambdaIn = ArrayContainsPredicate < TSource > ( propertyValues , property . Name , op ) ;
178-
179- return source . Where ( lambdaIn ) ;
180- }
181- else if ( op == FilterOperations . isnotnull || op == FilterOperations . isnull ) {
182- // {model}
183- var parameter = Expression . Parameter ( concreteType , "model" ) ;
184- // {model.Id}
185- var left = Expression . PropertyOrField ( parameter , property . Name ) ;
186- var right = Expression . Constant ( null ) ;
187-
188- var body = GetFilterExpressionLambda ( left , right , op ) ;
189- var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
190-
191- return source . Where ( lambda ) ;
192- }
193- else
194- { // convert the incoming value to the target value type
195- // "1" -> 1
196- var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , property . PropertyType ) ;
197- // {model}
198- var parameter = Expression . Parameter ( concreteType , "model" ) ;
199- // {model.Id}
200- var left = Expression . PropertyOrField ( parameter , property . Name ) ;
201- // {1}
202- var right = Expression . Constant ( convertedValue , property . PropertyType ) ;
203-
204- var body = GetFilterExpressionLambda ( left , right , op ) ;
205-
206- var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
207-
208- return source . Where ( lambda ) ;
209- }
210- }
211- catch ( FormatException )
212- {
213- throw new JsonApiException ( 400 , $ "Could not cast { filterQuery . PropertyValue } to { property . PropertyType . Name } ") ;
214- }
215- }
216-
217- public static IQueryable < TSource > Filter < TSource > ( this IQueryable < TSource > source , RelatedAttrQuery filterQuery )
218- {
219- if ( filterQuery == null )
220- return source ;
221-
222- var concreteType = typeof ( TSource ) ;
223- var relation = concreteType . GetProperty ( filterQuery . RelationshipAttribute . InternalRelationshipName ) ;
224- if ( relation == null )
225- throw new ArgumentException ( $ "'{ filterQuery . RelationshipAttribute . InternalRelationshipName } ' is not a valid relationship of '{ concreteType } '") ;
226-
227- var relatedType = filterQuery . RelationshipAttribute . Type ;
228- var relatedAttr = relatedType . GetProperty ( filterQuery . Attribute . InternalAttributeName ) ;
229- if ( relatedAttr == null )
230- throw new ArgumentException ( $ "'{ filterQuery . Attribute . InternalAttributeName } ' is not a valid attribute of '{ filterQuery . RelationshipAttribute . InternalRelationshipName } '") ;
231-
232- try
233- {
234- if ( filterQuery . FilterOperation == FilterOperations . @in || filterQuery . FilterOperation == FilterOperations . nin )
235- {
236- string [ ] propertyValues = filterQuery . PropertyValue . Split ( ',' ) ;
237- var lambdaIn = ArrayContainsPredicate < TSource > ( propertyValues , relatedAttr . Name , filterQuery . FilterOperation , relation . Name ) ;
238-
239- return source . Where ( lambdaIn ) ;
240- }
241- else
242- {
243- // convert the incoming value to the target value type
244- // "1" -> 1
245- var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , relatedAttr . PropertyType ) ;
246- // {model}
247- var parameter = Expression . Parameter ( concreteType , "model" ) ;
248-
249- // {model.Relationship}
250- var leftRelationship = Expression . PropertyOrField ( parameter , relation . Name ) ;
251-
252- // {model.Relationship.Attr}
253- var left = Expression . PropertyOrField ( leftRelationship , relatedAttr . Name ) ;
254-
255- // {1}
256- var right = Expression . Constant ( convertedValue , relatedAttr . PropertyType ) ;
257-
258- var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
259-
260- var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
261-
262- return source . Where ( lambda ) ;
263- }
264- }
265- catch ( FormatException )
266- {
267- throw new JsonApiException ( 400 , $ "Could not cast { filterQuery . PropertyValue } to { relatedAttr . PropertyType . Name } ") ;
268- }
112+ if ( filterQuery . FilterOperation == FilterOperations . @in || filterQuery . FilterOperation == FilterOperations . nin )
113+ return CallGenericWhereContainsMethod ( source , filterQuery ) ;
114+ else
115+ return CallGenericWhereMethod ( source , filterQuery ) ;
269116 }
270117
271- private static bool IsNullable ( Type type ) => type . IsGenericType && type . GetGenericTypeDefinition ( ) == typeof ( Nullable < > ) ;
272-
273-
274118 private static Expression GetFilterExpressionLambda ( Expression left , Expression right , FilterOperations operation )
275119 {
276120 Expression body ;
@@ -318,35 +162,6 @@ private static Expression GetFilterExpressionLambda(Expression left, Expression
318162 return body ;
319163 }
320164
321- private static Expression < Func < TSource , bool > > ArrayContainsPredicate < TSource > ( string [ ] propertyValues , string fieldname , FilterOperations op , string relationName = null )
322- {
323- ParameterExpression entity = Expression . Parameter ( typeof ( TSource ) , "entity" ) ;
324- MemberExpression member ;
325- if ( ! string . IsNullOrEmpty ( relationName ) )
326- {
327- var relation = Expression . PropertyOrField ( entity , relationName ) ;
328- member = Expression . Property ( relation , fieldname ) ;
329- }
330- else
331- member = Expression . Property ( entity , fieldname ) ;
332-
333- var method = ContainsMethod . MakeGenericMethod ( member . Type ) ;
334- var obj = TypeHelper . ConvertListType ( propertyValues , member . Type ) ;
335-
336- if ( op == FilterOperations . @in )
337- {
338- // Where(i => arr.Contains(i.column))
339- var contains = Expression . Call ( method , new Expression [ ] { Expression . Constant ( obj ) , member } ) ;
340- return Expression . Lambda < Func < TSource , bool > > ( contains , entity ) ;
341- }
342- else
343- {
344- // Where(i => !arr.Contains(i.column))
345- var notContains = Expression . Not ( Expression . Call ( method , new Expression [ ] { Expression . Constant ( obj ) , member } ) ) ;
346- return Expression . Lambda < Func < TSource , bool > > ( notContains , entity ) ;
347- }
348- }
349-
350165 public static IQueryable < TSource > Select < TSource > ( this IQueryable < TSource > source , List < string > columns )
351166 {
352167 if ( columns == null || columns . Count == 0 )
@@ -388,5 +203,141 @@ public static IQueryable<T> PageForward<T>(this IQueryable<T> source, int pageSi
388203
389204 return source ;
390205 }
206+
207+ #region Generic method calls
208+
209+ private static IOrderedQueryable < TSource > CallGenericOrderMethod < TSource > ( IQueryable < TSource > source , BaseAttrQuery baseAttrQuery , string method )
210+ {
211+ // {x}
212+ var parameter = Expression . Parameter ( typeof ( TSource ) , "x" ) ;
213+ MemberExpression member ;
214+ // {x.relationship.propertyName}
215+ if ( baseAttrQuery . IsAttributeOfRelationship )
216+ {
217+ var relation = Expression . PropertyOrField ( parameter , baseAttrQuery . RelationshipAttribute . InternalRelationshipName ) ;
218+ member = Expression . Property ( relation , baseAttrQuery . Attribute . InternalAttributeName ) ;
219+ }
220+ // {x.propertyName}
221+ else
222+ member = Expression . Property ( parameter , baseAttrQuery . Attribute . InternalAttributeName ) ;
223+
224+ // {x=>x.propertyName} or {x=>x.relationship.propertyName}
225+ var lambda = Expression . Lambda ( member , parameter ) ;
226+
227+ // REFLECTION: source.OrderBy(x => x.Property)
228+ var orderByMethod = typeof ( Queryable ) . GetMethods ( ) . First ( x => x . Name == method && x . GetParameters ( ) . Length == 2 ) ;
229+ var orderByGeneric = orderByMethod . MakeGenericMethod ( typeof ( TSource ) , member . Type ) ;
230+ var result = orderByGeneric . Invoke ( null , new object [ ] { source , lambda } ) ;
231+
232+ return ( IOrderedQueryable < TSource > ) result ;
233+ }
234+
235+ private static IQueryable < TSource > CallGenericWhereMethod < TSource > ( IQueryable < TSource > source , BaseAttrQuery filter )
236+ {
237+ var op = filter . FilterOperation ;
238+ var concreteType = typeof ( TSource ) ;
239+ PropertyInfo relationProperty = null ;
240+ PropertyInfo property = null ;
241+ MemberExpression left ;
242+ ConstantExpression right ;
243+
244+ // {model}
245+ var parameter = Expression . Parameter ( concreteType , "model" ) ;
246+ // Is relationship attribute
247+ if ( filter . IsAttributeOfRelationship )
248+ {
249+ relationProperty = concreteType . GetProperty ( filter . RelationshipAttribute . InternalRelationshipName ) ;
250+ if ( relationProperty == null )
251+ throw new ArgumentException ( $ "'{ filter . RelationshipAttribute . InternalRelationshipName } ' is not a valid relationship of '{ concreteType } '") ;
252+
253+ var relatedType = filter . RelationshipAttribute . Type ;
254+ property = relatedType . GetProperty ( filter . Attribute . InternalAttributeName ) ;
255+ if ( property == null )
256+ throw new ArgumentException ( $ "'{ filter . Attribute . InternalAttributeName } ' is not a valid attribute of '{ filter . RelationshipAttribute . InternalRelationshipName } '") ;
257+
258+ var leftRelationship = Expression . PropertyOrField ( parameter , filter . RelationshipAttribute . InternalRelationshipName ) ;
259+ // {model.Relationship}
260+ left = Expression . PropertyOrField ( leftRelationship , property . Name ) ;
261+ }
262+ // Is standalone attribute
263+ else
264+ {
265+ property = concreteType . GetProperty ( filter . Attribute . InternalAttributeName ) ;
266+ if ( property == null )
267+ throw new ArgumentException ( $ "'{ filter . Attribute . InternalAttributeName } ' is not a valid property of '{ concreteType } '") ;
268+
269+ // {model.Id}
270+ left = Expression . PropertyOrField ( parameter , property . Name ) ;
271+ }
272+
273+ try
274+ {
275+ if ( op == FilterOperations . isnotnull || op == FilterOperations . isnull )
276+ right = Expression . Constant ( null ) ;
277+ else
278+ {
279+ // convert the incoming value to the target value type
280+ // "1" -> 1
281+ var convertedValue = TypeHelper . ConvertType ( filter . PropertyValue , property . PropertyType ) ;
282+ // {1}
283+ right = Expression . Constant ( convertedValue , property . PropertyType ) ;
284+ }
285+
286+ var body = GetFilterExpressionLambda ( left , right , filter . FilterOperation ) ;
287+ var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
288+
289+ return source . Where ( lambda ) ;
290+ }
291+ catch ( FormatException )
292+ {
293+ throw new JsonApiException ( 400 , $ "Could not cast { filter . PropertyValue } to { property . PropertyType . Name } ") ;
294+ }
295+ }
296+
297+ private static IQueryable < TSource > CallGenericWhereContainsMethod < TSource > ( IQueryable < TSource > source , BaseAttrQuery filter )
298+ {
299+ var concreteType = typeof ( TSource ) ;
300+ var property = concreteType . GetProperty ( filter . Attribute . InternalAttributeName ) ;
301+
302+ try
303+ {
304+ var propertyValues = filter . PropertyValue . Split ( QueryConstants . COMMA ) ;
305+ ParameterExpression entity = Expression . Parameter ( concreteType , "entity" ) ;
306+ MemberExpression member ;
307+ if ( filter . IsAttributeOfRelationship )
308+ {
309+ var relation = Expression . PropertyOrField ( entity , filter . RelationshipAttribute . InternalRelationshipName ) ;
310+ member = Expression . Property ( relation , filter . Attribute . InternalAttributeName ) ;
311+ }
312+ else
313+ member = Expression . Property ( entity , filter . Attribute . InternalAttributeName ) ;
314+
315+ var method = ContainsMethod . MakeGenericMethod ( member . Type ) ;
316+ var obj = TypeHelper . ConvertListType ( propertyValues , member . Type ) ;
317+
318+ if ( filter . FilterOperation == FilterOperations . @in )
319+ {
320+ // Where(i => arr.Contains(i.column))
321+ var contains = Expression . Call ( method , new Expression [ ] { Expression . Constant ( obj ) , member } ) ;
322+ var lambda = Expression . Lambda < Func < TSource , bool > > ( contains , entity ) ;
323+
324+ return source . Where ( lambda ) ;
325+ }
326+ else
327+ {
328+ // Where(i => !arr.Contains(i.column))
329+ var notContains = Expression . Not ( Expression . Call ( method , new Expression [ ] { Expression . Constant ( obj ) , member } ) ) ;
330+ var lambda = Expression . Lambda < Func < TSource , bool > > ( notContains , entity ) ;
331+
332+ return source . Where ( lambda ) ;
333+ }
334+ }
335+ catch ( FormatException )
336+ {
337+ throw new JsonApiException ( 400 , $ "Could not cast { filter . PropertyValue } to { property . PropertyType . Name } ") ;
338+ }
339+ }
340+
341+ #endregion
391342 }
392343}
0 commit comments