1
+ using System . Collections . Generic ;
2
+ using System . Collections ;
3
+ using System . Linq . Dynamic . Core ;
4
+ using System . Linq . Dynamic . Core . Extensions ;
5
+ using System . Linq . Dynamic . Core . Validation ;
6
+ using System . Linq . Expressions ;
7
+ using System . Reflection ;
8
+ using JetBrains . Annotations ;
9
+ using System . Threading ;
10
+ using System . Threading . Tasks ;
11
+ #if NETSTANDARD
12
+ using Microsoft . EntityFrameworkCore . Query . Internal ;
13
+ #endif
14
+ #if WINDOWS_APP
15
+ using System ;
16
+ using System . Linq ;
17
+ #endif
18
+
19
+ namespace System . Linq . Dynamic . Core . EF
20
+ {
21
+ /// <summary>
22
+ /// Provides a set of static (Shared in Visual Basic) methods for querying data structures that implement <see cref="IQueryable"/>.
23
+ /// It allows dynamic string based querying. Very handy when, at compile time, you don't know the type of queries that will be generated,
24
+ /// or when downstream components only return column names to sort and filter by.
25
+ /// </summary>
26
+ public static class EntityFrameworkDynamicQueryableExtensions
27
+ {
28
+ #region AnyAsync
29
+ private static readonly MethodInfo _any = GetMethod ( nameof ( Queryable . Any ) ) ;
30
+
31
+ /// <summary>
32
+ /// Asynchronously determines whether a sequence contains any elements.
33
+ /// </summary>
34
+ /// <remarks>
35
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
36
+ /// that any asynchronous operations have completed before calling another method on this context.
37
+ /// </remarks>
38
+ /// <param name="source">
39
+ /// An <see cref="IQueryable{T}" /> to check for being empty.
40
+ /// </param>
41
+ /// <param name="cancellationToken">
42
+ /// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
43
+ /// </param>
44
+ /// <returns>
45
+ /// A task that represents the asynchronous operation.
46
+ /// The task result contains <c>true</c> if the source sequence contains any elements; otherwise, <c>false</c>.
47
+ /// </returns>
48
+ public static Task < bool > AnyAsync ( [ NotNull ] this IQueryable source , CancellationToken cancellationToken = default ( CancellationToken ) )
49
+ {
50
+ Check . NotNull ( source , nameof ( source ) ) ;
51
+
52
+ return ExecuteAsync < bool > ( _any , source , cancellationToken ) ;
53
+ }
54
+
55
+ private static readonly MethodInfo _anyPredicate = GetMethod ( nameof ( Queryable . Any ) , 1 ) ;
56
+
57
+ /// <summary>
58
+ /// Asynchronously determines whether any element of a sequence satisfies a condition.
59
+ /// </summary>
60
+ /// <remarks>
61
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
62
+ /// that any asynchronous operations have completed before calling another method on this context.
63
+ /// </remarks>
64
+ /// <param name="source">
65
+ /// An <see cref="IQueryable" /> whose elements to test for a condition.
66
+ /// </param>
67
+ /// <param name="predicate"> A function to test each element for a condition.</param>
68
+ /// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.</param>
69
+ /// <param name="cancellationToken">
70
+ /// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
71
+ /// </param>
72
+ /// <returns>
73
+ /// A task that represents the asynchronous operation.
74
+ /// The task result contains <c>true</c> if any elements in the source sequence pass the test in the specified
75
+ /// predicate; otherwise, <c>false</c>.
76
+ /// </returns>
77
+ public static Task < bool > AnyAsync ( [ NotNull ] this IQueryable source , [ NotNull ] string predicate , [ CanBeNull ] object [ ] args = null , CancellationToken cancellationToken = default ( CancellationToken ) )
78
+ {
79
+ Check . NotNull ( source , nameof ( source ) ) ;
80
+ Check . NotEmpty ( predicate , nameof ( predicate ) ) ;
81
+
82
+ bool createParameterCtor = source . IsLinqToObjects ( ) ;
83
+ LambdaExpression lambda = DynamicExpression . ParseLambda ( createParameterCtor , source . ElementType , null , predicate , args ) ;
84
+
85
+ return ExecuteAsync < bool > ( _anyPredicate , source , Expression . Quote ( lambda ) ) ;
86
+ }
87
+ #endregion AnyAsync
88
+
89
+ #region Private Helpers
90
+ // Copied from https://github.com/aspnet/EntityFramework/blob/9186d0b78a3176587eeb0f557c331f635760fe92/src/Microsoft.EntityFrameworkCore/EntityFrameworkQueryableExtensions.cs
91
+ private static Task < TResult > ExecuteAsync < TResult > ( MethodInfo operatorMethodInfo , IQueryable source , CancellationToken cancellationToken = default ( CancellationToken ) )
92
+ {
93
+ var provider = source . Provider as IAsyncQueryProvider ;
94
+
95
+ if ( provider != null )
96
+ {
97
+ if ( operatorMethodInfo . IsGenericMethod )
98
+ {
99
+ operatorMethodInfo = operatorMethodInfo . MakeGenericMethod ( source . ElementType ) ;
100
+ }
101
+
102
+ return provider . ExecuteAsync < TResult > (
103
+ Expression . Call ( null , operatorMethodInfo , source . Expression ) ,
104
+ cancellationToken ) ;
105
+ }
106
+
107
+ throw new InvalidOperationException ( Res . IQueryableProviderNotAsync ) ;
108
+ }
109
+
110
+ private static Task < TResult > ExecuteAsync < TResult > ( MethodInfo operatorMethodInfo , IQueryable source , LambdaExpression expression , CancellationToken cancellationToken = default ( CancellationToken ) )
111
+ => ExecuteAsync < TResult > ( operatorMethodInfo , source , Expression . Quote ( expression ) , cancellationToken ) ;
112
+
113
+ private static Task < TResult > ExecuteAsync < TResult > ( MethodInfo operatorMethodInfo , IQueryable source , Expression expression , CancellationToken cancellationToken = default ( CancellationToken ) )
114
+ {
115
+ var provider = source . Provider as IAsyncQueryProvider ;
116
+
117
+ if ( provider != null )
118
+ {
119
+ operatorMethodInfo
120
+ = operatorMethodInfo . GetGenericArguments ( ) . Length == 2
121
+ ? operatorMethodInfo . MakeGenericMethod ( source . ElementType , typeof ( TResult ) )
122
+ : operatorMethodInfo . MakeGenericMethod ( source . ElementType ) ;
123
+
124
+ return provider . ExecuteAsync < TResult > (
125
+ Expression . Call (
126
+ null ,
127
+ operatorMethodInfo ,
128
+ new [ ] { source . Expression , expression } ) ,
129
+ cancellationToken ) ;
130
+ }
131
+
132
+ throw new InvalidOperationException ( Res . IQueryableProviderNotAsync ) ;
133
+ }
134
+
135
+ private static MethodInfo GetMethod < TResult > ( string name , int parameterCount = 0 , Func < MethodInfo , bool > predicate = null ) =>
136
+ GetMethod ( name , parameterCount , mi => ( mi . ReturnType == typeof ( TResult ) ) && ( ( predicate == null ) || predicate ( mi ) ) ) ;
137
+
138
+ private static MethodInfo GetMethod ( string name , int parameterCount = 0 , Func < MethodInfo , bool > predicate = null ) =>
139
+ typeof ( Queryable ) . GetTypeInfo ( ) . GetDeclaredMethods ( name ) . Single ( mi => ( mi . GetParameters ( ) . Length == parameterCount + 1 ) && ( ( predicate == null ) || predicate ( mi ) ) ) ;
140
+ #endregion Private Helpers
141
+ }
142
+ }
0 commit comments