@@ -100,6 +100,60 @@ public static object Aggregate([NotNull] this IQueryable source, [NotNull] strin
100100 }
101101 #endregion Aggregate
102102
103+ #region All
104+ private static readonly MethodInfo _AllPredicate = GetMethod ( nameof ( Queryable . All ) , 1 ) ;
105+
106+ /// <summary>
107+ /// Determines whether all the elements of a sequence satisfy a condition.
108+ /// </summary>
109+ /// <remarks>
110+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
111+ /// that All asynchronous operations have completed before calling another method on this context.
112+ /// </remarks>
113+ /// <param name="source">
114+ /// An <see cref="IQueryable" /> to calculate the All of.
115+ /// </param>
116+ /// <param name="predicate">A projection function to apply to each element.</param>
117+ /// <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>
118+ /// <returns>
119+ /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false.
120+ /// </returns>
121+ [ PublicAPI ]
122+ public static bool All ( [ NotNull ] this IQueryable source , [ NotNull ] string predicate , [ CanBeNull ] params object [ ] args )
123+ {
124+ return All ( source , ParsingConfig . Default , predicate , args ) ;
125+ }
126+
127+ /// <summary>
128+ /// Determines whether all the elements of a sequence satisfy a condition.
129+ /// </summary>
130+ /// <remarks>
131+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
132+ /// that All asynchronous operations have completed before calling another method on this context.
133+ /// </remarks>
134+ /// <param name="source">
135+ /// An <see cref="IQueryable" /> to calculate the All of.
136+ /// </param>
137+ /// <param name="config">The <see cref="ParsingConfig"/>.</param>
138+ /// <param name="predicate">A projection function to apply to each element.</param>
139+ /// <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>
140+ /// <returns>
141+ /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false.
142+ /// </returns>
143+ [ PublicAPI ]
144+ public static bool All ( [ NotNull ] this IQueryable source , [ NotNull ] ParsingConfig config , [ NotNull ] string predicate , [ CanBeNull ] params object [ ] args )
145+ {
146+ Check . NotNull ( source , nameof ( source ) ) ;
147+ Check . NotNull ( config , nameof ( config ) ) ;
148+ Check . NotEmpty ( predicate , nameof ( predicate ) ) ;
149+
150+ bool createParameterCtor = SupportsLinqToObjects ( config , source ) ;
151+ LambdaExpression lambda = DynamicExpressionParser . ParseLambda ( createParameterCtor , source . ElementType , null , predicate , args ) ;
152+
153+ return Execute < bool > ( _AllPredicate , source , Expression . Quote ( lambda ) ) ;
154+ }
155+ #endregion AllAsync
156+
103157 #region Any
104158 private static readonly MethodInfo _any = GetMethod ( nameof ( Queryable . Any ) ) ;
105159
@@ -173,6 +227,79 @@ public static bool Any([NotNull] this IQueryable source, [NotNull] LambdaExpress
173227 }
174228 #endregion Any
175229
230+ #region Average
231+ /// <summary>
232+ /// Computes the average of a sequence of numeric values.
233+ /// </summary>
234+ /// <param name="source">A sequence of numeric values to calculate the average of.</param>
235+ /// <example>
236+ /// <code language="cs">
237+ /// IQueryable queryable = employees.AsQueryable();
238+ /// var result1 = queryable.Average();
239+ /// var result2 = queryable.Select("Roles.Average()");
240+ /// </code>
241+ /// </example>
242+ /// <returns>The average of the values in the sequence.</returns>
243+ [ PublicAPI ]
244+ public static double Average ( [ NotNull ] this IQueryable source )
245+ {
246+ Check . NotNull ( source , nameof ( source ) ) ;
247+
248+ var average = GetMethod ( nameof ( Queryable . Average ) , source . ElementType , typeof ( double ) ) ;
249+ return Execute < double > ( average , source ) ;
250+ }
251+
252+ /// <summary>
253+ /// Computes the average of a sequence of numeric values.
254+ /// </summary>
255+ /// <param name="source">A sequence of numeric values to calculate the average of.</param>
256+ /// <param name="config">The <see cref="ParsingConfig"/>.</param>
257+ /// <param name="predicate">A function to test each element for a condition.</param>
258+ /// <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>
259+ /// <example>
260+ /// <code language="cs">
261+ /// IQueryable queryable = employees.AsQueryable();
262+ /// var result = queryable.Average("Income");
263+ /// </code>
264+ /// </example>
265+ /// <returns>The average of the values in the sequence.</returns>
266+ [ PublicAPI ]
267+ public static double Average ( [ NotNull ] this IQueryable source , [ NotNull ] ParsingConfig config , [ NotNull ] string predicate , params object [ ] args )
268+ {
269+ Check . NotNull ( source , nameof ( source ) ) ;
270+ Check . NotNull ( config , nameof ( config ) ) ;
271+ Check . NotEmpty ( predicate , nameof ( predicate ) ) ;
272+
273+ bool createParameterCtor = SupportsLinqToObjects ( config , source ) ;
274+ LambdaExpression lambda = DynamicExpressionParser . ParseLambda ( config , createParameterCtor , source . ElementType , null , predicate , args ) ;
275+
276+ return Average ( source , lambda ) ;
277+ }
278+
279+ /// <inheritdoc cref="Average(IQueryable, ParsingConfig, string, object[])"/>
280+ [ PublicAPI ]
281+ public static double Average ( [ NotNull ] this IQueryable source , [ NotNull ] string predicate , [ CanBeNull ] params object [ ] args )
282+ {
283+ return Average ( source , ParsingConfig . Default , predicate , args ) ;
284+ }
285+
286+ /// <summary>
287+ /// Computes the average of a sequence of numeric values.
288+ /// </summary>
289+ /// <param name="source">A sequence of numeric values to calculate the average of.</param>
290+ /// <param name="lambda">A Lambda Expression.</param>
291+ /// <returns>The average of the values in the sequence.</returns>
292+ [ PublicAPI ]
293+ public static double Average ( [ NotNull ] this IQueryable source , [ NotNull ] LambdaExpression lambda )
294+ {
295+ Check . NotNull ( source , nameof ( source ) ) ;
296+ Check . NotNull ( lambda , nameof ( lambda ) ) ;
297+
298+ var averageSelector = GetMethod ( nameof ( Queryable . Average ) , lambda . GetReturnType ( ) , typeof ( double ) , 1 ) ;
299+ return Execute < double > ( averageSelector , source , lambda ) ;
300+ }
301+ #endregion Average
302+
176303 #region AsEnumerable
177304#if NET35
178305 /// <summary>
@@ -1808,13 +1935,74 @@ public static IQueryable SkipWhile([NotNull] this IQueryable source, [NotNull] s
18081935 /// Computes the sum of a sequence of numeric values.
18091936 /// </summary>
18101937 /// <param name="source">A sequence of numeric values to calculate the sum of.</param>
1938+ /// <example>
1939+ /// <code language="cs">
1940+ /// IQueryable queryable = employees.AsQueryable();
1941+ /// var result1 = queryable.Sum();
1942+ /// var result2 = queryable.Select("Roles.Sum()");
1943+ /// </code>
1944+ /// </example>
18111945 /// <returns>The sum of the values in the sequence.</returns>
1946+ [ PublicAPI ]
18121947 public static object Sum ( [ NotNull ] this IQueryable source )
18131948 {
18141949 Check . NotNull ( source , nameof ( source ) ) ;
18151950
1816- var optimized = OptimizeExpression ( Expression . Call ( typeof ( Queryable ) , nameof ( Queryable . Sum ) , null , source . Expression ) ) ;
1817- return source . Provider . Execute ( optimized ) ;
1951+ var sum = GetMethod ( nameof ( Queryable . Sum ) , source . ElementType ) ;
1952+ return Execute < object > ( sum , source ) ;
1953+ }
1954+
1955+ /// <summary>
1956+ /// Computes the sum of a sequence of numeric values.
1957+ /// </summary>
1958+ /// <param name="source">A sequence of numeric values to calculate the sum of.</param>
1959+ /// <param name="config">The <see cref="ParsingConfig"/>.</param>
1960+ /// <param name="predicate">A function to test each element for a condition.</param>
1961+ /// <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>
1962+ /// <example>
1963+ /// <code language="cs">
1964+ /// IQueryable queryable = employees.AsQueryable();
1965+ /// var result = queryable.Sum("Income");
1966+ /// </code>
1967+ /// </example>
1968+ /// <returns>The sum of the values in the sequence.</returns>
1969+ [ PublicAPI ]
1970+ public static object Sum ( [ NotNull ] this IQueryable source , [ NotNull ] ParsingConfig config , [ NotNull ] string predicate , params object [ ] args )
1971+ {
1972+ Check . NotNull ( source , nameof ( source ) ) ;
1973+ Check . NotNull ( config , nameof ( config ) ) ;
1974+ Check . NotEmpty ( predicate , nameof ( predicate ) ) ;
1975+
1976+ bool createParameterCtor = SupportsLinqToObjects ( config , source ) ;
1977+ LambdaExpression lambda = DynamicExpressionParser . ParseLambda ( config , createParameterCtor , source . ElementType , null , predicate , args ) ;
1978+
1979+ var sumSelector = GetMethod ( nameof ( Queryable . Sum ) , lambda . GetReturnType ( ) , 1 ) ;
1980+
1981+ return Execute < object > ( sumSelector , source , lambda ) ;
1982+ }
1983+
1984+ /// <inheritdoc cref="Sum(IQueryable, ParsingConfig, string, object[])"/>
1985+ [ PublicAPI ]
1986+ public static object Sum ( [ NotNull ] this IQueryable source , [ NotNull ] string predicate , [ CanBeNull ] params object [ ] args )
1987+ {
1988+ return Sum ( source , ParsingConfig . Default , predicate , args ) ;
1989+ }
1990+
1991+ /// <summary>
1992+ /// Computes the sum of a sequence of numeric values.
1993+ /// </summary>
1994+ /// <param name="source">A sequence of numeric values to calculate the sum of.</param>
1995+ /// <param name="lambda">A Lambda Expression.</param>
1996+ /// <returns>The sum of the values in the sequence.</returns>
1997+ [ PublicAPI ]
1998+ public static object Sum ( [ NotNull ] this IQueryable source , [ NotNull ] LambdaExpression lambda )
1999+ {
2000+ Check . NotNull ( source , nameof ( source ) ) ;
2001+ Check . NotNull ( lambda , nameof ( lambda ) ) ;
2002+
2003+ var sumSelector = GetMethod ( nameof ( Queryable . Sum ) , lambda . GetReturnType ( ) , 1 ) ;
2004+
2005+ return Execute < object > ( sumSelector , source , lambda ) ;
18182006 }
18192007 #endregion Sum
18202008
@@ -2056,13 +2244,6 @@ private static void CheckOuterAndInnerTypes(ParsingConfig config, bool createPar
20562244 // If types are not the same, try to convert to Nullable and generate new LambdaExpression
20572245 if ( outerSelectorReturnType != innerSelectorReturnType )
20582246 {
2059- //var outerSelectorReturnTypeInfo = outerSelectorReturnType.GetTypeInfo();
2060- //var innerSelectorReturnTypeInfo = innerSelectorReturnType.GetTypeInfo();
2061- //if (outerSelectorReturnTypeInfo.BaseType == typeof(DynamicClass) && innerSelectorReturnTypeInfo.BaseType == typeof(DynamicClass))
2062- //{
2063-
2064- //}
2065-
20662247 if ( TypeHelper . IsNullableType ( outerSelectorReturnType ) && ! TypeHelper . IsNullableType ( innerSelectorReturnType ) )
20672248 {
20682249 innerSelectorReturnType = ExpressionParser . ToNullableType ( innerSelectorReturnType ) ;
@@ -2125,7 +2306,9 @@ private static TResult Execute<TResult>(MethodInfo operatorMethodInfo, IQueryabl
21252306 }
21262307
21272308 var optimized = OptimizeExpression ( Expression . Call ( null , operatorMethodInfo , source . Expression ) ) ;
2128- return source . Provider . Execute < TResult > ( optimized ) ;
2309+ var result = source . Provider . Execute ( optimized ) ;
2310+
2311+ return ( TResult ) Convert . ChangeType ( result , typeof ( TResult ) ) ;
21292312 }
21302313
21312314 private static object Execute ( MethodInfo operatorMethodInfo , IQueryable source , LambdaExpression expression )
@@ -2151,14 +2334,22 @@ private static TResult Execute<TResult>(MethodInfo operatorMethodInfo, IQueryabl
21512334 : operatorMethodInfo . MakeGenericMethod ( source . ElementType ) ;
21522335
21532336 var optimized = OptimizeExpression ( Expression . Call ( null , operatorMethodInfo , source . Expression , expression ) ) ;
2154- return source . Provider . Execute < TResult > ( optimized ) ;
2337+ var result = source . Provider . Execute ( optimized ) ;
2338+
2339+ return ( TResult ) Convert . ChangeType ( result , typeof ( TResult ) ) ;
21552340 }
21562341
21572342 private static MethodInfo GetGenericMethod ( string name )
21582343 {
21592344 return typeof ( Queryable ) . GetTypeInfo ( ) . GetDeclaredMethods ( name ) . Single ( mi => mi . IsGenericMethod ) ;
21602345 }
21612346
2347+ private static MethodInfo GetMethod ( string name , Type argumentType , Type returnType , int parameterCount = 0 , Func < MethodInfo , bool > predicate = null ) =>
2348+ GetMethod ( name , returnType , parameterCount , mi => mi . ToString ( ) . Contains ( argumentType . ToString ( ) ) && ( ( predicate == null ) || predicate ( mi ) ) ) ;
2349+
2350+ private static MethodInfo GetMethod ( string name , Type returnType , int parameterCount = 0 , Func < MethodInfo , bool > predicate = null ) =>
2351+ GetMethod ( name , parameterCount , mi => ( mi . ReturnType == returnType ) && ( ( predicate == null ) || predicate ( mi ) ) ) ;
2352+
21622353 private static MethodInfo GetMethod ( string name , int parameterCount = 0 , Func < MethodInfo , bool > predicate = null )
21632354 {
21642355 try
@@ -2168,7 +2359,7 @@ private static MethodInfo GetMethod(string name, int parameterCount = 0, Func<Me
21682359 }
21692360 catch ( Exception ex )
21702361 {
2171- throw new Exception ( "Method not found: " + name , ex ) ;
2362+ throw new Exception ( "Specific method not found: " + name , ex ) ;
21722363 }
21732364 }
21742365 #endregion Private Helpers
0 commit comments