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