@@ -113,24 +113,44 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
113113
114114 var concreteType = typeof ( TSource ) ;
115115 var property = concreteType . GetProperty ( filterQuery . FilteredAttribute . InternalAttributeName ) ;
116+ var op = filterQuery . FilterOperation ;
116117
117118 if ( property == null )
118119 throw new ArgumentException ( $ "'{ filterQuery . FilteredAttribute . InternalAttributeName } ' is not a valid property of '{ concreteType } '") ;
119120
120121 try
121122 {
122- if ( filterQuery . FilterOperation == FilterOperations . @in || filterQuery . FilterOperation == FilterOperations . nin )
123+ if ( op == FilterOperations . @in || op == FilterOperations . nin )
123124 {
124125 string [ ] propertyValues = filterQuery . PropertyValue . Split ( ',' ) ;
125- var lambdaIn = ArrayContainsPredicate < TSource > ( propertyValues , property . Name , filterQuery . FilterOperation ) ;
126+ var lambdaIn = ArrayContainsPredicate < TSource > ( propertyValues , property . Name , op ) ;
126127
127128 return source . Where ( lambdaIn ) ;
128129 }
130+ else if ( op == FilterOperations . @is || op == FilterOperations . isnot ) {
131+ var parameter = Expression . Parameter ( concreteType , "model" ) ;
132+ // {model.Id}
133+ var left = Expression . PropertyOrField ( parameter , property . Name ) ;
134+ var right = Expression . Constant ( filterQuery . PropertyValue , typeof ( string ) ) ;
135+
136+ var body = GetFilterExpressionLambda ( left , right , op ) ;
137+ var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
138+
139+ return source . Where ( lambda ) ;
140+ }
129141 else
130142 {
131143 var isNullabe = IsNullable ( property . PropertyType ) ;
132144 var propertyValue = filterQuery . PropertyValue ;
133- var value = isNullabe && propertyValue == "" ? null : propertyValue ;
145+ var value = propertyValue ;
146+
147+ if ( op == FilterOperations . @isnot || op == FilterOperations . isnot )
148+ {
149+ if ( isNullabe && propertyValue == "null" )
150+ {
151+ value = null ;
152+ }
153+ }
134154
135155 // convert the incoming value to the target value type
136156 // "1" -> 1
@@ -142,7 +162,7 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
142162 // {1}
143163 var right = Expression . Constant ( convertedValue , property . PropertyType ) ;
144164
145- var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
165+ var body = GetFilterExpressionLambda ( left , right , op ) ;
146166
147167 var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
148168
@@ -244,6 +264,14 @@ private static Expression GetFilterExpressionLambda(Expression left, Expression
244264 case FilterOperations . ne :
245265 body = Expression . NotEqual ( left , right ) ;
246266 break ;
267+ case FilterOperations . isnot :
268+ // {model.Id != null}
269+ body = Expression . NotEqual ( left , right ) ;
270+ break ;
271+ case FilterOperations . @is :
272+ // {model.Id == null}
273+ body = Expression . Equal ( left , right ) ;
274+ break ;
247275 default :
248276 throw new JsonApiException ( 500 , $ "Unknown filter operation { operation } ") ;
249277 }
0 commit comments