1
1
namespace FSharp.Data.GraphQL.Server.Middleware
2
2
3
3
open System
4
+ open FSharp.Data .GraphQL
4
5
5
6
/// A filter definition for a field value.
6
7
type FieldFilter < 'Val > = { FieldName : string ; Value : 'Val }
@@ -26,6 +27,8 @@ open System.Linq
26
27
open System.Linq .Expressions
27
28
open System.Runtime .InteropServices
28
29
open System.Reflection
30
+ open System.Collections
31
+ open System.Collections .Generic
29
32
30
33
type private CompareDiscriminatorExpression < 'T , 'D > = Expression< Func< 'T, 'D, bool>>
31
34
@@ -139,6 +142,7 @@ module ObjectListFilter =
139
142
Expression.Call ( whereMethod, [| query.Expression; Expression.Lambda< Func< 'T, bool>> ( predicate, param) |])
140
143
141
144
let private stringType = typeof< string>
145
+ let private genericIEnumerableType = typedefof< IEnumerable<_>>
142
146
let private StringStartsWithMethod = stringType.GetMethod ( " StartsWith" , [| stringType |])
143
147
let private StringEndsWithMethod = stringType.GetMethod ( " EndsWith" , [| stringType |])
144
148
let private StringContainsMethod = stringType.GetMethod ( " Contains" , [| stringType |])
@@ -175,29 +179,89 @@ module ObjectListFilter =
175
179
static member op_Implicit ( parameter : ParameterExpression ) = SourceExpression ( parameter :> Expression)
176
180
static member op_Implicit ( ``member`` : MemberExpression ) = SourceExpression ( `` member `` :> Expression)
177
181
178
- let rec buildFilterExpr ( param : SourceExpression ) buildTypeDiscriminatorCheck filter : Expression =
179
- let build = buildFilterExpr param buildTypeDiscriminatorCheck
182
+ //let iequtableType = typedefof<IEquatable<_>>
183
+ let equalsMethod =
184
+ typeof< obj>
185
+ |> _. GetMethods( BindingFlags.Instance ||| BindingFlags.Public)
186
+ |> Seq.where ( fun m -> m.Name = " Equals" )
187
+ |> Seq.head
188
+
189
+ let rec buildFilterExpr isEnumerableQuery ( param : SourceExpression ) buildTypeDiscriminatorCheck filter : Expression =
190
+ let build = buildFilterExpr isEnumerableQuery param buildTypeDiscriminatorCheck
191
+ let (| NoCast | Enumerable | NonEnumerableCast |) value =
192
+ if obj.ReferenceEquals ( value, null ) then
193
+ NoCast
194
+ else
195
+ if isEnumerableQuery then Enumerable
196
+ else NonEnumerableCast ( value.GetType ())
197
+ let unsafeConvertTo ``type`` ``member`` = Expression.Convert ( Expression.Convert( `` member `` , typeof< obj>), `` type `` )
198
+
180
199
match filter with
200
+ | Not ( Equals f) ->
201
+ let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
202
+ let ``const`` = Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value)
203
+ if isEnumerableQuery && f.Value <> null then
204
+ Expression.Not ( Expression.Call ( `` const `` , equalsMethod, `` member `` ))
205
+ else
206
+ Expression.NotEqual ( `` member `` , `` const `` )
181
207
| Not f -> f |> build |> Expression.Not :> Expression
182
208
| And ( f1, f2) -> Expression.AndAlso ( build f1, build f2)
183
209
| Or ( f1, f2) -> Expression.OrElse ( build f1, build f2)
184
- | Equals f -> Expression.Equal ( Expression.PropertyOrField ( param, f.FieldName), Expression.Constant ( f.Value))
185
- | GreaterThan f -> Expression.GreaterThan ( Expression.PropertyOrField ( param, f.FieldName), Expression.Constant ( f.Value))
186
- | LessThan f -> Expression.LessThan ( Expression.PropertyOrField ( param, f.FieldName), Expression.Constant ( f.Value))
187
- | GreaterThanOrEqual f -> Expression.GreaterThanOrEqual ( Expression.PropertyOrField ( param, f.FieldName), Expression.Constant ( f.Value))
188
- | LessThanOrEqual f -> Expression.LessThanOrEqual ( Expression.PropertyOrField ( param, f.FieldName), Expression.Constant ( f.Value))
210
+ | Equals f ->
211
+ let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
212
+ match f.Value with
213
+ | NoCast ->
214
+ Expression.Equal ( `` member `` , Expression.Constant f.Value)
215
+ | Enumerable ->
216
+ Expression.Equal ( `` member `` , Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
217
+ | NonEnumerableCast `` type `` ->
218
+ Expression.Equal (( unsafeConvertTo `` type `` `` member `` ), Expression.Constant f.Value)
219
+ | GreaterThan f ->
220
+ let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
221
+ let ``const`` = Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value)
222
+ if isEnumerableQuery then
223
+ Expression.GreaterThan ( `` member `` , Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
224
+ else
225
+ Expression.GreaterThan ( Expression.Convert ( Expression.Convert( `` member `` , typeof< obj>), f.Value.GetType ()), Expression.Constant f.Value)
226
+ | LessThan f ->
227
+ let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
228
+ match f.Value with
229
+ | NoCast ->
230
+ Expression.LessThan ( `` member `` , Expression.Constant f.Value)
231
+ | Enumerable ->
232
+ Expression.LessThan ( `` member `` , Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
233
+ | NonEnumerableCast `` type `` ->
234
+ Expression.LessThan (( unsafeConvertTo `` type `` `` member `` ), Expression.Constant f.Value)
235
+ | GreaterThanOrEqual f ->
236
+ let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
237
+ match f.Value with
238
+ | NoCast ->
239
+ Expression.GreaterThanOrEqual ( `` member `` , Expression.Constant f.Value)
240
+ | Enumerable ->
241
+ Expression.GreaterThanOrEqual ( `` member `` , Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
242
+ | NonEnumerableCast `` type `` ->
243
+ Expression.GreaterThanOrEqual (( unsafeConvertTo `` type `` `` member `` ), Expression.Constant f.Value)
244
+ | LessThanOrEqual f ->
245
+ let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
246
+ match f.Value with
247
+ | NoCast ->
248
+ Expression.LessThanOrEqual ( `` member `` , Expression.Constant f.Value)
249
+ | Enumerable ->
250
+ Expression.LessThanOrEqual ( `` member `` , Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
251
+ | NonEnumerableCast `` type `` ->
252
+ Expression.LessThanOrEqual (( unsafeConvertTo `` type `` `` member `` ), Expression.Constant f.Value)
189
253
| StartsWith f ->
190
254
let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
191
255
if `` member `` .Type = stringType then
192
- Expression.Call ( `` member `` , StringStartsWithMethod, Expression.Constant ( f.Value))
256
+ Expression.Call ( `` member `` , StringStartsWithMethod, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
193
257
else
194
- Expression.Call ( Expression.Convert ( `` member `` , stringType), StringStartsWithMethod, Expression.Constant ( f.Value))
258
+ Expression.Call ( Expression.Convert ( `` member `` , stringType), StringStartsWithMethod, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
195
259
| EndsWith f ->
196
260
let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
197
261
if `` member `` .Type = stringType then
198
- Expression.Call ( `` member `` , StringEndsWithMethod, Expression.Constant ( f.Value))
262
+ Expression.Call ( `` member `` , StringEndsWithMethod, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
199
263
else
200
- Expression.Call ( Expression.Convert ( `` member `` , stringType), StringEndsWithMethod, Expression.Constant ( f.Value))
264
+ Expression.Call ( Expression.Convert ( `` member `` , stringType), StringEndsWithMethod, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
201
265
| Contains f ->
202
266
let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
203
267
let isEnumerable ( memberType : Type ) =
@@ -216,35 +280,38 @@ module ObjectListFilter =
216
280
| value -> value.GetType()
217
281
let castedMember =
218
282
if itemType = valueType then `` member `` :> Expression
219
- else
283
+ elif isEnumerableQuery then
220
284
let castMethod = getEnumerableCastMethod valueType
221
285
Expression.Call ( castMethod, `` member `` )
286
+ else
287
+ let castedEnumerableType = genericIEnumerableType.MakeGenericType ([| valueType |])
288
+ unsafeConvertTo castedEnumerableType `` member ``
222
289
match getCollectionInstanceContainsMethod memberType with
223
290
| ValueNone ->
224
291
let enumerableContains = getEnumerableContainsMethod valueType
225
- Expression.Call ( enumerableContains, castedMember, Expression.Constant ( f.Value))
292
+ Expression.Call ( enumerableContains, castedMember, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
226
293
| ValueSome instanceContainsMethod ->
227
- Expression.Call ( castedMember, instanceContainsMethod, Expression.Constant ( f.Value))
294
+ Expression.Call ( castedMember, instanceContainsMethod, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
228
295
match `` member `` .Member with
229
296
| :? PropertyInfo as prop when prop.PropertyType |> isEnumerable -> callContains prop.PropertyType
230
297
| :? FieldInfo as field when field.FieldType |> isEnumerable -> callContains field.FieldType
231
298
| _ ->
232
299
if `` member `` .Type = stringType then
233
- Expression.Call ( `` member `` , StringContainsMethod, Expression.Constant ( f.Value))
300
+ Expression.Call ( `` member `` , StringContainsMethod, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
234
301
else
235
- Expression.Call ( Expression.Convert ( `` member `` , stringType), StringContainsMethod, Expression.Constant ( f.Value))
302
+ Expression.Call ( Expression.Convert ( `` member `` , stringType), StringContainsMethod, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value))
236
303
| In f when not ( f.Value.IsEmpty) ->
237
304
let ``member`` = Expression.PropertyOrField ( param, f.FieldName)
238
305
let enumerableContains = getEnumerableContainsMethod typeof< IComparable>
239
- Expression.Call ( enumerableContains, Expression.Constant ( f.Value), Expression.Convert ( `` member `` , typeof< IComparable>))
306
+ Expression.Call ( enumerableContains, Expression.Constant ( Values.normalizeOptional `` member `` .Type f.Value), Expression.Convert ( `` member `` , typeof< IComparable>))
240
307
| In f -> Expression.Constant ( true )
241
308
| OfTypes types ->
242
309
types
243
310
|> Seq.map ( fun t -> buildTypeDiscriminatorCheck param t)
244
311
|> Seq.reduce ( fun acc expr -> Expression.OrElse ( acc, expr))
245
312
| FilterField f ->
246
313
let paramExpr = Expression.PropertyOrField ( param, f.FieldName)
247
- buildFilterExpr ( SourceExpression paramExpr) buildTypeDiscriminatorCheck f.Value
314
+ buildFilterExpr isEnumerableQuery ( SourceExpression paramExpr) buildTypeDiscriminatorCheck f.Value
248
315
249
316
type private CompareDiscriminatorExpressionVisitor < 'T , 'D > (
250
317
compareDiscriminator : CompareDiscriminatorExpression< 'T, 'D>,
@@ -260,7 +327,10 @@ module ObjectListFilter =
260
327
else
261
328
node :> Expression
262
329
330
+ let enumerableQueryType = typedefof< EnumerableQuery<_>>
331
+
263
332
let apply ( options : ObjectListFilterLinqOptions < 'T , 'D >) ( filter : ObjectListFilter ) ( query : IQueryable < 'T >) =
333
+ let isEnumerableQuery = query.GetType() .GetGenericTypeDefinition() = enumerableQueryType
264
334
// Helper for discriminator comparison
265
335
let buildTypeDiscriminatorCheck ( param : SourceExpression ) ( t : Type ) =
266
336
match options.CompareDiscriminator, options.GetDiscriminatorValue with
@@ -290,7 +360,7 @@ module ObjectListFilter =
290
360
replacer.Visit discExpr.Body
291
361
let queryExpr =
292
362
let param = Expression.Parameter ( typeof< 'T>, " x" )
293
- let body = buildFilterExpr ( SourceExpression param) buildTypeDiscriminatorCheck filter
363
+ let body = buildFilterExpr isEnumerableQuery ( SourceExpression param) buildTypeDiscriminatorCheck filter
294
364
whereExpr< 'T> query param body
295
365
// Create and execute the final expression
296
366
query.Provider.CreateQuery< 'T> ( queryExpr)
0 commit comments