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 ;
@@ -11,6 +12,23 @@ namespace JsonApiDotNetCore.Extensions
1112 // ReSharper disable once InconsistentNaming
1213 public static class IQueryableExtensions
1314 {
15+ private static MethodInfo _containsMethod ;
16+ private static MethodInfo ContainsMethod
17+ {
18+ get
19+ {
20+ if ( _containsMethod == null )
21+ {
22+ _containsMethod = typeof ( Enumerable )
23+ . GetMethods ( BindingFlags . Static | BindingFlags . Public )
24+ . Where ( m => m . Name == nameof ( Enumerable . Contains ) && m . GetParameters ( ) . Count ( ) == 2 )
25+ . First ( ) ;
26+ }
27+ return _containsMethod ;
28+ }
29+ }
30+
31+
1432 public static IQueryable < TSource > Sort < TSource > ( this IQueryable < TSource > source , List < SortQuery > sortQueries )
1533 {
1634 if ( sortQueries == null || sortQueries . Count == 0 )
@@ -101,21 +119,30 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
101119
102120 try
103121 {
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 ) ;
122+ if ( filterQuery . FilterOperation == FilterOperations . @in )
123+ {
124+ string [ ] propertyValues = filterQuery . PropertyValue . Split ( ',' ) ;
125+ var lambdaIn = ArrayContainsPredicate < TSource > ( propertyValues , property . Name ) ;
126+
127+ return source . Where ( lambdaIn ) ;
128+ }
129+ else
130+ { // convert the incoming value to the target value type
131+ // "1" -> 1
132+ var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , property . PropertyType ) ;
133+ // {model}
134+ var parameter = Expression . Parameter ( concreteType , "model" ) ;
135+ // {model.Id}
136+ var left = Expression . PropertyOrField ( parameter , property . Name ) ;
137+ // {1}
138+ var right = Expression . Constant ( convertedValue , property . PropertyType ) ;
139+
140+ var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
141+
142+ var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
143+
144+ return source . Where ( lambda ) ;
145+ }
119146 }
120147 catch ( FormatException )
121148 {
@@ -140,26 +167,36 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
140167
141168 try
142169 {
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" ) ;
170+ if ( filterQuery . FilterOperation == FilterOperations . @in )
171+ {
172+ string [ ] propertyValues = filterQuery . PropertyValue . Split ( ',' ) ;
173+ var lambdaIn = ArrayContainsPredicate < TSource > ( propertyValues , relatedAttr . Name , relation . Name ) ;
148174
149- // {model.Relationship}
150- var leftRelationship = Expression . PropertyOrField ( parameter , relation . Name ) ;
175+ return source . Where ( lambdaIn ) ;
176+ }
177+ else
178+ {
179+ // convert the incoming value to the target value type
180+ // "1" -> 1
181+ var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , relatedAttr . PropertyType ) ;
182+ // {model}
183+ var parameter = Expression . Parameter ( concreteType , "model" ) ;
151184
152- // {model.Relationship.Attr }
153- var left = Expression . PropertyOrField ( leftRelationship , relatedAttr . Name ) ;
185+ // {model.Relationship}
186+ var leftRelationship = Expression . PropertyOrField ( parameter , relation . Name ) ;
154187
155- // {1 }
156- var right = Expression . Constant ( convertedValue , relatedAttr . PropertyType ) ;
188+ // {model.Relationship.Attr }
189+ var left = Expression . PropertyOrField ( leftRelationship , relatedAttr . Name ) ;
157190
158- var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
191+ // {1}
192+ var right = Expression . Constant ( convertedValue , relatedAttr . PropertyType ) ;
159193
160- var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
194+ var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
161195
162- return source . Where ( lambda ) ;
196+ var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
197+
198+ return source . Where ( lambda ) ;
199+ }
163200 }
164201 catch ( FormatException )
165202 {
@@ -206,6 +243,25 @@ private static Expression GetFilterExpressionLambda(Expression left, Expression
206243 return body ;
207244 }
208245
246+ private static Expression < Func < TSource , bool > > ArrayContainsPredicate < TSource > ( string [ ] propertyValues , string fieldname , string relationName = null )
247+ {
248+ ParameterExpression entity = Expression . Parameter ( typeof ( TSource ) , "entity" ) ;
249+ MemberExpression member ;
250+ if ( ! string . IsNullOrEmpty ( relationName ) )
251+ {
252+ var relation = Expression . PropertyOrField ( entity , relationName ) ;
253+ member = Expression . Property ( relation , fieldname ) ;
254+ }
255+ else
256+ member = Expression . Property ( entity , fieldname ) ;
257+
258+ var method = ContainsMethod . MakeGenericMethod ( member . Type ) ;
259+ var obj = TypeHelper . ConvertListType ( propertyValues , member . Type ) ;
260+
261+ var exprContains = Expression . Call ( method , new Expression [ ] { Expression . Constant ( obj ) , member } ) ;
262+ return Expression . Lambda < Func < TSource , bool > > ( exprContains , entity ) ;
263+ }
264+
209265 public static IQueryable < TSource > Select < TSource > ( this IQueryable < TSource > source , List < string > columns )
210266 {
211267 if ( columns == null || columns . Count == 0 )
0 commit comments