22using System . Collections . Generic ;
33using System . Linq ;
44using System . Linq . Expressions ;
5+ using System . Reflection ;
56using JsonApiDotNetCore . Internal ;
67using JsonApiDotNetCore . Internal . Query ;
78using JsonApiDotNetCore . Services ;
@@ -101,21 +102,30 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
101102
102103 try
103104 {
104- // convert the incoming value to the target value type
105- // "1" -> 1
106- var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , property . PropertyType ) ;
107- // {model}
108- var parameter = Expression . Parameter ( concreteType , "model" ) ;
109- // {model.Id}
110- var left = Expression . PropertyOrField ( parameter , property . Name ) ;
111- // {1}
112- var right = Expression . Constant ( convertedValue , property . PropertyType ) ;
113-
114- var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
115-
116- var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
117-
118- return source . Where ( lambda ) ;
105+ if ( filterQuery . FilterOperation == FilterOperations . @in )
106+ {
107+ string [ ] propertyValues = filterQuery . PropertyValue . Split ( ',' ) ;
108+ var lambdaIn = ArrayContainsPredicate < TSource > ( propertyValues , property . Name ) ;
109+
110+ return source . Where ( lambdaIn ) ;
111+ }
112+ else
113+ { // convert the incoming value to the target value type
114+ // "1" -> 1
115+ var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , property . PropertyType ) ;
116+ // {model}
117+ var parameter = Expression . Parameter ( concreteType , "model" ) ;
118+ // {model.Id}
119+ var left = Expression . PropertyOrField ( parameter , property . Name ) ;
120+ // {1}
121+ var right = Expression . Constant ( convertedValue , property . PropertyType ) ;
122+
123+ var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
124+
125+ var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
126+
127+ return source . Where ( lambda ) ;
128+ }
119129 }
120130 catch ( FormatException )
121131 {
@@ -140,26 +150,36 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
140150
141151 try
142152 {
143- // convert the incoming value to the target value type
144- // "1" -> 1
145- var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , relatedAttr . PropertyType ) ;
146- // {model}
147- var parameter = Expression . Parameter ( concreteType , "model" ) ;
153+ if ( filterQuery . FilterOperation == FilterOperations . @in )
154+ {
155+ string [ ] propertyValues = filterQuery . PropertyValue . Split ( ',' ) ;
156+ var lambdaIn = ArrayContainsPredicate < TSource > ( propertyValues , relatedAttr . Name , relation . Name ) ;
157+
158+ return source . Where ( lambdaIn ) ;
159+ }
160+ else
161+ {
162+ // convert the incoming value to the target value type
163+ // "1" -> 1
164+ var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , relatedAttr . PropertyType ) ;
165+ // {model}
166+ var parameter = Expression . Parameter ( concreteType , "model" ) ;
148167
149- // {model.Relationship}
150- var leftRelationship = Expression . PropertyOrField ( parameter , relation . Name ) ;
168+ // {model.Relationship}
169+ var leftRelationship = Expression . PropertyOrField ( parameter , relation . Name ) ;
151170
152- // {model.Relationship.Attr}
153- var left = Expression . PropertyOrField ( leftRelationship , relatedAttr . Name ) ;
171+ // {model.Relationship.Attr}
172+ var left = Expression . PropertyOrField ( leftRelationship , relatedAttr . Name ) ;
154173
155- // {1}
156- var right = Expression . Constant ( convertedValue , relatedAttr . PropertyType ) ;
174+ // {1}
175+ var right = Expression . Constant ( convertedValue , relatedAttr . PropertyType ) ;
157176
158- var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
177+ var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
159178
160- var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
179+ var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
161180
162- return source . Where ( lambda ) ;
181+ return source . Where ( lambda ) ;
182+ }
163183 }
164184 catch ( FormatException )
165185 {
@@ -206,6 +226,35 @@ private static Expression GetFilterExpressionLambda(Expression left, Expression
206226 return body ;
207227 }
208228
229+ private static Expression < Func < TSource , bool > > ArrayContainsPredicate < TSource > ( string [ ] propertyValues , string fieldname , string relationName = null )
230+ {
231+ ParameterExpression entity = Expression . Parameter ( typeof ( TSource ) , "entity" ) ;
232+ MemberExpression member ;
233+ if ( ! string . IsNullOrEmpty ( relationName ) )
234+ {
235+ var relation = Expression . PropertyOrField ( entity , relationName ) ;
236+ member = Expression . Property ( relation , fieldname ) ;
237+ }
238+ else
239+ member = Expression . Property ( entity , fieldname ) ;
240+
241+ var containsMethods = typeof ( Enumerable ) . GetMethods ( BindingFlags . Static | BindingFlags . Public ) . Where ( m => m . Name == "Contains" ) ;
242+ MethodInfo method = null ;
243+ foreach ( var m in containsMethods )
244+ {
245+ if ( m . GetParameters ( ) . Count ( ) == 2 )
246+ {
247+ method = m ;
248+ break ;
249+ }
250+ }
251+ method = method . MakeGenericMethod ( member . Type ) ;
252+ var obj = TypeHelper . ConvertListType ( propertyValues , member . Type ) ;
253+
254+ var exprContains = Expression . Call ( method , new Expression [ ] { Expression . Constant ( obj ) , member } ) ;
255+ return Expression . Lambda < Func < TSource , bool > > ( exprContains , entity ) ;
256+ }
257+
209258 public static IQueryable < TSource > Select < TSource > ( this IQueryable < TSource > source , List < string > columns )
210259 {
211260 if ( columns == null || columns . Count == 0 )
0 commit comments