Skip to content

Commit c9dd83e

Browse files
author
Viktor Tochonov
committed
Implemented logic for different MemberInfo types in ObjectListFilter Contains case
1 parent 3c0dc43 commit c9dd83e

File tree

1 file changed

+109
-62
lines changed

1 file changed

+109
-62
lines changed

src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs

Lines changed: 109 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,75 @@ type ObjectListFilter =
2020
| FilterField of FieldFilter<ObjectListFilter>
2121
| NoFilter
2222

23+
open System.Linq
24+
open System.Linq.Expressions
25+
open System.Runtime.InteropServices
26+
open System.Reflection
27+
28+
/// <remarks>
29+
/// Represents a value that can be either None or Some.
30+
/// </remarks>
31+
32+
/// <summary>
33+
/// Allows to specify discriminator comparison or discriminator getter
34+
/// and a function that return discriminator value depending on entity type
35+
/// </summary>
36+
/// <example id="item-1"><code lang="fsharp">
37+
/// // discriminator custom condition
38+
/// let result () =
39+
/// queryable.Apply(
40+
/// filter,
41+
/// ObjectListFilterLinqOptions (
42+
/// (fun entity discriminator -> entity.Discriminator.StartsWith discriminator),
43+
/// (function
44+
/// | t when Type.(=)(t, typeof<Cat>) -> "cat+v1"
45+
/// | t when Type.(=)(t, typeof<Dog>) -> "dog+v1")
46+
/// )
47+
/// )
48+
/// </code></example>
49+
/// <example id="item-2"><code lang="fsharp">
50+
/// // discriminator equals
51+
/// let result () =
52+
/// queryable.Apply(
53+
/// filter,
54+
/// ObjectListFilterLinqOptions (
55+
/// (fun entity -> entity.Discriminator),
56+
/// (function
57+
/// | t when Type.(=)(t, typeof<Cat>) -> "cat"
58+
/// | t when Type.(=)(t, typeof<Dog>) -> "dog")
59+
/// )
60+
/// )
61+
/// </code></example>
62+
[<Struct>]
63+
type ObjectListFilterLinqOptions<'T, 'D> (
64+
[<Optional>] compareDiscriminator : Expression<Func<'T, 'D, bool>> | null,
65+
[<Optional>] getDiscriminatorValue : (Type -> 'D) | null,
66+
[<Optional>] serializeMemberName : (MemberInfo -> string) | null
67+
) =
68+
69+
member _.CompareDiscriminator = compareDiscriminator |> ValueOption.ofObj
70+
member _.GetDiscriminatorValue = getDiscriminatorValue |> ValueOption.ofObj
71+
//member _.SerializeMemberName = serializeMemberName |> ValueOption.ofObj
72+
73+
static member None = ObjectListFilterLinqOptions<'T, 'D> (null, null, null)
74+
75+
static member GetCompareDiscriminator (getDiscriminatorValue : Expression<Func<'T, 'D>>) =
76+
let tParam = Expression.Parameter (typeof<'T>, "x")
77+
let dParam = Expression.Parameter (typeof<'D>, "d")
78+
let body = Expression.Equal(Expression.Invoke(getDiscriminatorValue, tParam), dParam)
79+
Expression.Lambda<Func<'T, 'D, bool>> (body, tParam, dParam)
80+
81+
new (getDiscriminator : Expression<Func<'T, 'D>>) = ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, null, null)
82+
new (compareDiscriminator : Expression<Func<'T, 'D, bool>>) = ObjectListFilterLinqOptions<'T, 'D> (compareDiscriminator, null, null)
83+
new (getDiscriminatorValue : Type -> 'D) = ObjectListFilterLinqOptions<'T, 'D> (null, getDiscriminatorValue, null)
84+
//new (serializeMemberName : MemberInfo -> string) = ObjectListFilterLinqOptions<'T, 'D> (null, null, serializeMemberName)
85+
86+
new (getDiscriminator : Expression<Func<'T, 'D>>, getDiscriminatorValue : Type -> 'D) = ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, getDiscriminatorValue, null)
87+
88+
//new (getDiscriminator : Expression<Func<'T, 'D>>, serializeMemberName : MemberInfo -> string) = ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, null, serializeMemberName)
89+
//new (compareDiscriminator : Expression<Func<'T, 'D, bool>>, serializeMemberName : MemberInfo -> string) = ObjectListFilterLinqOptions<'T, 'D> (compareDiscriminator, null, serializeMemberName)
90+
//new (getDiscriminatorValue : Type -> 'D, serializeMemberName : MemberInfo -> string) = ObjectListFilterLinqOptions<'T, 'D> (null, getDiscriminatorValue, serializeMemberName)
91+
2392
/// Contains tooling for working with ObjectListFilter.
2493
module ObjectListFilter =
2594
/// Contains operators for building and comparing ObjectListFilter values.
@@ -54,34 +123,6 @@ module ObjectListFilter =
54123
/// Creates a new ObjectListFilter representing a NOT opreation for the existing one.
55124
let ( !!! ) filter = Not filter
56125

57-
open System.Linq
58-
open System.Linq.Expressions
59-
open System.Runtime.InteropServices
60-
open System.Reflection
61-
62-
[<AutoOpen>]
63-
module ObjectListFilterExtensions =
64-
65-
66-
//// discriminator custom condition
67-
//let a () =
68-
// filter.Apply(
69-
// queryable,
70-
// <@ fun e d -> e.Discriminator.StartsWith d @>,
71-
// function
72-
// | t when Type.(=)(t, typeof<Complex>) -> ResidentialPropertiesConstants.Discriminators.Complex
73-
// | t when Type.(=)(t, typeof<Building>) -> ResidentialPropertiesConstants.Discriminators.Building
74-
// )
75-
//// discriminator equals
76-
//let b () =
77-
// filter.Apply(
78-
// queryable,
79-
// <@ fun e -> e.Discriminator @>,
80-
// function
81-
// | t when Type.(=)(t, typeof<Complex>) -> ResidentialPropertiesConstants.Discriminators.Complex
82-
// | t when Type.(=)(t, typeof<Building>) -> ResidentialPropertiesConstants.Discriminators.Building
83-
// )
84-
85126
let private genericWhereMethod =
86127
typeof<Queryable>.GetMethods ()
87128
|> Seq.where (fun m -> m.Name = "Where")
@@ -98,6 +139,18 @@ module ObjectListFilterExtensions =
98139
let private StringStartsWithMethod = typeof<string>.GetMethod ("StartsWith", [| typeof<string> |])
99140
let private StringEndsWithMethod = typeof<string>.GetMethod ("EndsWith", [| typeof<string> |])
100141
let private StringContainsMethod = typeof<string>.GetMethod ("Contains", [| typeof<string> |])
142+
let private getEnumerableContainsMethod (memberType : Type) =
143+
match memberType.GetMethods(BindingFlags.Instance &&& BindingFlags.Public).FirstOrDefault(fun m -> m.Name = "Contains" && m.GetParameters().Length = 2) with
144+
| null ->
145+
match typeof<Enumerable>.GetMethods(BindingFlags.Static ||| BindingFlags.Public).FirstOrDefault(fun m -> m.Name = "Contains" && m.GetParameters().Length = 2) with
146+
| null -> raise (MissingMemberException "Static 'Contains' method with 2 parameters not found on 'Enumerable' class")
147+
| containsGenericStaticMethod ->
148+
if memberType.IsGenericType && memberType.GenericTypeArguments.Length = 1 then
149+
containsGenericStaticMethod.MakeGenericMethod(memberType.GenericTypeArguments)
150+
else
151+
let ienumerable = memberType.GetType().GetInterfaces().First(fun i -> i.FullName.StartsWith "System.Collections.Generic.IEnumerable`1")
152+
containsGenericStaticMethod.MakeGenericMethod([| ienumerable.GenericTypeArguments[0] |])
153+
| instanceContainsMethod -> instanceContainsMethod
101154

102155
let getField (param : ParameterExpression) fieldName = Expression.PropertyOrField (param, fieldName)
103156

@@ -125,7 +178,18 @@ module ObjectListFilterExtensions =
125178
| EndsWith f ->
126179
Expression.Call (Expression.PropertyOrField (param, f.FieldName), StringEndsWithMethod, Expression.Constant (f.Value))
127180
| Contains f ->
128-
Expression.Call (Expression.PropertyOrField (param, f.FieldName), StringContainsMethod, Expression.Constant (f.Value))
181+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
182+
let isEnumerable (memberType: Type) =
183+
not (Type.(=)(memberType, typeof<string>))
184+
&& typeof<System.Collections.IEnumerable>.IsAssignableFrom(memberType)
185+
&& memberType.GetInterfaces().Any(fun i -> i.FullName.StartsWith "System.Collections.Generic.IEnumerable`1")
186+
match ``member``.Member with
187+
| :? PropertyInfo as prop when prop.PropertyType |> isEnumerable ->
188+
Expression.Call (getEnumerableContainsMethod prop.PropertyType, Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
189+
| :? FieldInfo as field when field.FieldType |> isEnumerable ->
190+
Expression.Call (getEnumerableContainsMethod field.FieldType, Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
191+
| _ ->
192+
Expression.Call (``member``, StringContainsMethod, Expression.Constant (f.Value))
129193
| OfTypes types ->
130194
types
131195
|> Seq.map (fun t -> buildTypeDiscriminatorCheck param t)
@@ -134,39 +198,7 @@ module ObjectListFilterExtensions =
134198
let paramExpr = Expression.PropertyOrField (param, f.FieldName)
135199
buildFilterExpr (SourceExpression paramExpr) buildTypeDiscriminatorCheck f.Value
136200

137-
[<Struct>]
138-
type ObjectListFilterLinqOptions<'T, 'D> (
139-
[<Optional>] compareDiscriminator : Expression<Func<'T, 'D, bool>> | null,
140-
[<Optional>] getDiscriminatorValue : (Type -> 'D) | null,
141-
[<Optional>] serializeMemberName : (MemberInfo -> string) | null) =
142-
143-
member _.CompareDiscriminator = compareDiscriminator |> ValueOption.ofObj
144-
member _.GetDiscriminatorValue = getDiscriminatorValue |> ValueOption.ofObj
145-
//member _.SerializeMemberName = serializeMemberName |> ValueOption.ofObj
146-
147-
static member None = ObjectListFilterLinqOptions<'T, 'D> (null, null, null)
148-
149-
static member GetCompareDiscriminator (getDiscriminatorValue : Expression<Func<'T, 'D>>) =
150-
let tParam = Expression.Parameter (typeof<'T>, "x")
151-
let dParam = Expression.Parameter (typeof<'D>, "d")
152-
let body = Expression.Equal(Expression.Invoke(getDiscriminatorValue, tParam), dParam)
153-
Expression.Lambda<Func<'T, 'D, bool>> (body, tParam, dParam)
154-
155-
new (getDiscriminator : Expression<Func<'T, 'D>>) = ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, null, null)
156-
new (compareDiscriminator : Expression<Func<'T, 'D, bool>>) = ObjectListFilterLinqOptions<'T, 'D> (compareDiscriminator, null, null)
157-
new (getDiscriminatorValue : Type -> 'D) = ObjectListFilterLinqOptions<'T, 'D> (null, getDiscriminatorValue, null)
158-
//new (serializeMemberName : MemberInfo -> string) = ObjectListFilterLinqOptions<'T, 'D> (null, null, serializeMemberName)
159-
160-
new (getDiscriminator : Expression<Func<'T, 'D>>, getDiscriminatorValue : Type -> 'D) = ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, getDiscriminatorValue, null)
161-
162-
//new (getDiscriminator : Expression<Func<'T, 'D>>, serializeMemberName : MemberInfo -> string) = ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, null, serializeMemberName)
163-
//new (compareDiscriminator : Expression<Func<'T, 'D, bool>>, serializeMemberName : MemberInfo -> string) = ObjectListFilterLinqOptions<'T, 'D> (compareDiscriminator, null, serializeMemberName)
164-
//new (getDiscriminatorValue : Type -> 'D, serializeMemberName : MemberInfo -> string) = ObjectListFilterLinqOptions<'T, 'D> (null, getDiscriminatorValue, serializeMemberName)
165-
166-
type ObjectListFilter with
167-
168-
member filter.Apply<'T, 'D> (query : IQueryable<'T>, [<Optional>] options : ObjectListFilterLinqOptions<'T, 'D>) =
169-
201+
let apply (options : ObjectListFilterLinqOptions<'T, 'D>) (filter : ObjectListFilter) (query : IQueryable<'T>) =
170202
match filter with
171203
| NoFilter -> query
172204
| _ ->
@@ -196,3 +228,18 @@ type ObjectListFilter with
196228
whereExpr<'T> query param body
197229
// Create and execute the final expression
198230
query.Provider.CreateQuery<'T> (queryExpr)
231+
232+
[<AutoOpen>]
233+
module ObjectListFilterExtensions =
234+
235+
open ObjectListFilter
236+
237+
type ObjectListFilter with
238+
239+
member inline filter.ApplyTo<'T, 'D> (query : IQueryable<'T>, [<Optional>] options : ObjectListFilterLinqOptions<'T, 'D>) =
240+
apply options filter query
241+
242+
type IQueryable<'T> with
243+
244+
member inline query.Apply (filter : ObjectListFilter, [<Optional>] options : ObjectListFilterLinqOptions<'T, 'D>) =
245+
apply options filter query

0 commit comments

Comments
 (0)