diff --git a/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs b/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs
index 38f4a6cb..1564452f 100644
--- a/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs
+++ b/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs
@@ -1,4 +1,5 @@
using ConsoleAppEF2.Database;
+using Microsoft.EntityFrameworkCore.DynamicLinq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -183,6 +184,14 @@ static void Main(string[] args)
{
Console.WriteLine(e);
}
+
+ var a1 = context.Cars.Select(c => c.Key).AverageAsync().Result;
+ var a2 = context.Cars.Select("Key").AverageAsync().Result;
+
+ // var a3 = context.Cars.AverageAsync(c => c.Key).Result;
+ var a4 = context.Cars.AverageAsync("Key").Result;
+
+ int end = 0;
}
private static void LikeTests(TestContext context, ParsingConfig config)
diff --git a/src-console/ConsoleApp_net452_EF6_Effort/Program.cs b/src-console/ConsoleApp_net452_EF6_Effort/Program.cs
index efbf1281..324329ca 100644
--- a/src-console/ConsoleApp_net452_EF6_Effort/Program.cs
+++ b/src-console/ConsoleApp_net452_EF6_Effort/Program.cs
@@ -15,7 +15,7 @@ static void Main(string[] args)
using (var context = new KendoGridDbContext(connection))
{
- context.KendoGridCountry.Add(new Country { Code = "NL", Name = "Nederland" });
+ context.KendoGridCountry.Add(new Country { Id = 1000, Code = "NL", Name = "Nederland" });
var main1 = new MainCompany { Name = "Main1" };
context.KendoGridMainCompany.Add(main1);
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq/EFDynamicQueryableExtensions.cs b/src/Microsoft.EntityFrameworkCore.DynamicLinq/EFDynamicQueryableExtensions.cs
index d11ef3e9..afd46754 100644
--- a/src/Microsoft.EntityFrameworkCore.DynamicLinq/EFDynamicQueryableExtensions.cs
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq/EFDynamicQueryableExtensions.cs
@@ -29,24 +29,60 @@ public static class EntityFrameworkDynamicQueryableExtensions
{
private static readonly TraceSource TraceSource = new TraceSource(typeof(EntityFrameworkDynamicQueryableExtensions).Name);
- private static Expression OptimizeExpression(Expression expression)
+ #region AllAsync
+ private static readonly MethodInfo _AllPredicate = GetMethod(nameof(Queryable.All), 1);
+
+ ///
+ /// Asynchronously determines whether all the elements of a sequence satisfy a condition.
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
+ /// that All asynchronous operations have completed before calling another method on this context.
+ ///
+ ///
+ /// An to calculate the All of.
+ ///
+ /// A projection function to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ ///
+ /// A task that represents the asynchronous operation. The task result contains true if every element of the source sequence passes the test in the specified predicate; otherwise, false.
+ ///
+ [PublicAPI]
+ public static Task AllAsync([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args)
{
- if (ExtensibilityPoint.QueryOptimizer != null)
- {
- var optimized = ExtensibilityPoint.QueryOptimizer(expression);
+ return AllAsync(source, predicate, default(CancellationToken), args);
+ }
-#if !(WINDOWS_APP45x || SILVERLIGHT)
- if (optimized != expression)
- {
- TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Expression before : {0}", expression);
- TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Expression after : {0}", optimized);
- }
-#endif
- return optimized;
- }
+ ///
+ /// Asynchronously determines whether all the elements of a sequence satisfy a condition.
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
+ /// that All asynchronous operations have completed before calling another method on this context.
+ ///
+ ///
+ /// An to calculate the All of.
+ ///
+ /// A projection function to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ ///
+ /// A task that represents the asynchronous operation. The task result contains true if every element of the source sequence passes the test in the specified predicate; otherwise, false.
+ ///
+ [PublicAPI]
+ public static Task AllAsync([NotNull] this IQueryable source, [NotNull] string predicate, CancellationToken cancellationToken = default(CancellationToken), [CanBeNull] params object[] args)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotEmpty(predicate, nameof(predicate));
+ Check.NotNull(cancellationToken, nameof(cancellationToken));
- return expression;
+ LambdaExpression lambda = DynamicExpressionParser.ParseLambda(false, source.ElementType, null, predicate, args);
+
+ return ExecuteAsync(_AllPredicate, source, Expression.Quote(lambda), cancellationToken);
}
+ #endregion AllAsync
#region AnyAsync
private static readonly MethodInfo _any = GetMethod(nameof(Queryable.Any));
@@ -65,8 +101,7 @@ private static Expression OptimizeExpression(Expression expression)
/// A to observe while waiting for the task to complete.
///
///
- /// A task that represents the asynchronous operation.
- /// The task result contains true if the source sequence contains any elements; otherwise, false.
+ /// A task that represents the asynchronous operation. The task result contains true if the source sequence contains any elements; otherwise, false.
///
[PublicAPI]
public static Task AnyAsync([NotNull] this IQueryable source, CancellationToken cancellationToken = default(CancellationToken))
@@ -89,12 +124,10 @@ private static Expression OptimizeExpression(Expression expression)
///
/// An whose elements to test for a condition.
///
- /// A function to test each element for a condition.
+ /// A function to test each element for a condition.
/// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
///
- /// A task that represents the asynchronous operation.
- /// The task result contains true if any elements in the source sequence pass the test in the specified
- /// predicate; otherwise, false.
+ /// A task that represents the asynchronous operation. The task result contains true if the source sequence contains any elements; otherwise, false.
///
[PublicAPI]
public static Task AnyAsync([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args)
@@ -112,15 +145,13 @@ public static Task AnyAsync([NotNull] this IQueryable source, [NotNull] st
///
/// An whose elements to test for a condition.
///
- /// A function to test each element for a condition.
+ /// A function to test each element for a condition.
/// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
///
/// A to observe while waiting for the task to complete.
///
///
- /// A task that represents the asynchronous operation.
- /// The task result contains true if any elements in the source sequence pass the test in the specified
- /// predicate; otherwise, false.
+ /// A task that represents the asynchronous operation. The task result contains true if the source sequence contains any elements; otherwise, false.
///
[PublicAPI]
public static Task AnyAsync([NotNull] this IQueryable source, [NotNull] string predicate, CancellationToken cancellationToken = default(CancellationToken), [CanBeNull] params object[] args)
@@ -135,6 +166,92 @@ public static Task AnyAsync([NotNull] this IQueryable source, [NotNull] st
}
#endregion AnyAsync
+ #region AverageAsync
+ private static readonly MethodInfo _average = GetMethod(nameof(Queryable.Average));
+
+ ///
+ /// Asynchronously computes the average of a sequence of values.
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
+ /// that any asynchronous operations have completed before calling another method on this context.
+ ///
+ ///
+ /// An to calculate the average of.
+ ///
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ ///
+ /// A task that represents the asynchronous operation. The task result contains the average of the sequence of values.
+ ///
+ [PublicAPI]
+ public static Task AverageAsync([NotNull] this IQueryable source, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(cancellationToken, nameof(cancellationToken));
+
+ return ExecuteAsync(_average, source, cancellationToken);
+ }
+
+ private static readonly MethodInfo _averagePredicate = GetMethod(nameof(Queryable.Average), 1);
+
+ ///
+ /// Asynchronously computes the average of a sequence of values that is obtained by invoking a projection function on each element of the input sequence.
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
+ /// that Average asynchronous operations have completed before calling another method on this context.
+ ///
+ ///
+ /// An to calculate the average of.
+ ///
+ /// A projection function to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ ///
+ /// A task that represents the asynchronous operation.
+ /// The task result contains true if Average elements in the source sequence pass the test in the specified
+ /// predicate; otherwise, false.
+ ///
+ [PublicAPI]
+ public static Task AverageAsync([NotNull] this IQueryable source, [NotNull] string selector, [CanBeNull] params object[] args)
+ {
+ return AverageAsync(source, selector, default(CancellationToken), args);
+ }
+
+ ///
+ /// Asynchronously computes the average of a sequence of values that is obtained by invoking a projection function on each element of the input sequence.
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
+ /// that Average asynchronous operations have completed before calling another method on this context.
+ ///
+ ///
+ /// An to calculate the average of.
+ ///
+ /// A projection function to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ ///
+ /// A task that represents the asynchronous operation.
+ /// The task result contains true if Average elements in the source sequence pass the test in the specified
+ /// predicate; otherwise, false.
+ ///
+ [PublicAPI]
+ public static Task AverageAsync([NotNull] this IQueryable source, [NotNull] string selector, CancellationToken cancellationToken = default(CancellationToken), [CanBeNull] params object[] args)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotEmpty(selector, nameof(selector));
+ Check.NotNull(cancellationToken, nameof(cancellationToken));
+
+ LambdaExpression lambda = DynamicExpressionParser.ParseLambda(false, source.ElementType, null, selector, args);
+
+ return ExecuteAsync(_averagePredicate, source, Expression.Quote(lambda), cancellationToken);
+ }
+ #endregion AverageAsync
+
#region Count
private static readonly MethodInfo _count = GetMethod(nameof(Queryable.Count));
@@ -662,7 +779,6 @@ public static Task SingleOrDefaultAsync([NotNull] this IQueryable sourc
#endregion SingleOrDefault
#region SumAsync
-
///
/// Asynchronously computes the sum of a sequence of values.
///
@@ -749,28 +865,9 @@ public static Task SumAsync([NotNull] this IQueryable source, Cancellat
#endregion SumAsync
#region Private Helpers
- // Copied from https://github.com/aspnet/EntityFramework/blob/9186d0b78a3176587eeb0f557c331f635760fe92/src/Microsoft.EntityFrameworkCore/EntityFrameworkQueryableExtensions.cs
- //private static Task ExecuteAsync(MethodInfo operatorMethodInfo, IQueryable source, CancellationToken cancellationToken = default(CancellationToken))
- //{
- // var provider = source.Provider as IAsyncQueryProvider;
-
- // if (provider != null)
- // {
- // if (operatorMethodInfo.IsGenericMethod)
- // {
- // operatorMethodInfo = operatorMethodInfo.MakeGenericMethod(source.ElementType);
- // }
-
- // return provider.ExecuteAsync(
- // Expression.Call(null, operatorMethodInfo, source.Expression),
- // cancellationToken);
- // }
-
- // throw new InvalidOperationException(Res.IQueryableProviderNotAsync);
- //}
private static readonly MethodInfo _executeAsyncMethod =
typeof(EntityFrameworkDynamicQueryableExtensions)
-#if NETSTANDARD
+#if NETSTANDARD || UAP10_0
.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
.Single(m => m.Name == nameof(ExecuteAsync) && m.GetParameters().Select(p => p.ParameterType).SequenceEqual(new[] { typeof(MethodInfo), typeof(IQueryable), typeof(CancellationToken) }));
#else
@@ -814,11 +911,11 @@ public static Task SumAsync([NotNull] this IQueryable source, Cancellat
private static readonly MethodInfo _executeAsyncMethodWithExpression =
typeof(EntityFrameworkDynamicQueryableExtensions)
-#if NETSTANDARD
+#if NETSTANDARD || UAP10_0
.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
.Single(m => m.Name == nameof(ExecuteAsync) && m.GetParameters().Select(p => p.ParameterType).SequenceEqual(new[] { typeof(MethodInfo), typeof(IQueryable), typeof(Expression), typeof(CancellationToken) }));
#else
- .GetMethod(nameof(ExecuteAsync), BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(MethodInfo), typeof(IQueryable), typeof(Expression), typeof(CancellationToken)}, null);
+ .GetMethod(nameof(ExecuteAsync), BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(MethodInfo), typeof(IQueryable), typeof(Expression), typeof(CancellationToken) }, null);
#endif
private static Task ExecuteDynamicAsync(MethodInfo operatorMethodInfo, IQueryable source, Expression expression, CancellationToken cancellationToken = default(CancellationToken))
@@ -861,6 +958,25 @@ private static MethodInfo GetMethod(string name, Type returnType, int parameterC
private static MethodInfo GetMethod(string name, int parameterCount = 0, Func predicate = null) =>
typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).First(mi => (mi.GetParameters().Length == parameterCount + 1) && ((predicate == null) || predicate(mi)));
-#endregion Private Helpers
+
+ private static Expression OptimizeExpression(Expression expression)
+ {
+ if (ExtensibilityPoint.QueryOptimizer != null)
+ {
+ var optimized = ExtensibilityPoint.QueryOptimizer(expression);
+
+#if !(WINDOWS_APP45x || SILVERLIGHT)
+ if (optimized != expression)
+ {
+ TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Expression before : {0}", expression);
+ TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Expression after : {0}", optimized);
+ }
+#endif
+ return optimized;
+ }
+
+ return expression;
+ }
+ #endregion Private Helpers
}
}
diff --git a/src/System.Linq.Dynamic.Core/Compatibility/LambdaExpressionExtensions.cs b/src/System.Linq.Dynamic.Core/Compatibility/LambdaExpressionExtensions.cs
new file mode 100644
index 00000000..d018c44b
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core/Compatibility/LambdaExpressionExtensions.cs
@@ -0,0 +1,15 @@
+// ReSharper disable once CheckNamespace
+namespace System.Linq.Expressions
+{
+ internal static class LambdaExpressionExtensions
+ {
+ public static Type GetReturnType(this LambdaExpression lambdaExpression)
+ {
+#if !NET35
+ return lambdaExpression.ReturnType;
+#else
+ return lambdaExpression.Body.Type;
+#endif
+ }
+ }
+}
diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
index bd58c7c1..2da1c69f 100644
--- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
+++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
@@ -100,6 +100,60 @@ public static object Aggregate([NotNull] this IQueryable source, [NotNull] strin
}
#endregion Aggregate
+ #region All
+ private static readonly MethodInfo _AllPredicate = GetMethod(nameof(Queryable.All), 1);
+
+ ///
+ /// Determines whether all the elements of a sequence satisfy a condition.
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
+ /// that All asynchronous operations have completed before calling another method on this context.
+ ///
+ ///
+ /// An to calculate the All of.
+ ///
+ /// A projection function to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ ///
+ /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false.
+ ///
+ [PublicAPI]
+ public static bool All([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args)
+ {
+ return All(source, ParsingConfig.Default, predicate, args);
+ }
+
+ ///
+ /// Determines whether all the elements of a sequence satisfy a condition.
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
+ /// that All asynchronous operations have completed before calling another method on this context.
+ ///
+ ///
+ /// An to calculate the All of.
+ ///
+ /// The .
+ /// A projection function to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ ///
+ /// true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false.
+ ///
+ [PublicAPI]
+ public static bool All([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] string predicate, [CanBeNull] params object[] args)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(config, nameof(config));
+ Check.NotEmpty(predicate, nameof(predicate));
+
+ bool createParameterCtor = SupportsLinqToObjects(config, source);
+ LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args);
+
+ return Execute(_AllPredicate, source, Expression.Quote(lambda));
+ }
+ #endregion AllAsync
+
#region Any
private static readonly MethodInfo _any = GetMethod(nameof(Queryable.Any));
@@ -173,6 +227,79 @@ public static bool Any([NotNull] this IQueryable source, [NotNull] LambdaExpress
}
#endregion Any
+ #region Average
+ ///
+ /// Computes the average of a sequence of numeric values.
+ ///
+ /// A sequence of numeric values to calculate the average of.
+ ///
+ ///
+ /// IQueryable queryable = employees.AsQueryable();
+ /// var result1 = queryable.Average();
+ /// var result2 = queryable.Select("Roles.Average()");
+ ///
+ ///
+ /// The average of the values in the sequence.
+ [PublicAPI]
+ public static double Average([NotNull] this IQueryable source)
+ {
+ Check.NotNull(source, nameof(source));
+
+ var average = GetMethod(nameof(Queryable.Average), source.ElementType, typeof(double));
+ return Execute(average, source);
+ }
+
+ ///
+ /// Computes the average of a sequence of numeric values.
+ ///
+ /// A sequence of numeric values to calculate the average of.
+ /// The .
+ /// A function to test each element for a condition.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ ///
+ ///
+ /// IQueryable queryable = employees.AsQueryable();
+ /// var result = queryable.Average("Income");
+ ///
+ ///
+ /// The average of the values in the sequence.
+ [PublicAPI]
+ public static double Average([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] string predicate, params object[] args)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(config, nameof(config));
+ Check.NotEmpty(predicate, nameof(predicate));
+
+ bool createParameterCtor = SupportsLinqToObjects(config, source);
+ LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args);
+
+ return Average(source, lambda);
+ }
+
+ ///
+ [PublicAPI]
+ public static double Average([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args)
+ {
+ return Average(source, ParsingConfig.Default, predicate, args);
+ }
+
+ ///
+ /// Computes the average of a sequence of numeric values.
+ ///
+ /// A sequence of numeric values to calculate the average of.
+ /// A Lambda Expression.
+ /// The average of the values in the sequence.
+ [PublicAPI]
+ public static double Average([NotNull] this IQueryable source, [NotNull] LambdaExpression lambda)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(lambda, nameof(lambda));
+
+ var averageSelector = GetMethod(nameof(Queryable.Average), lambda.GetReturnType(), typeof(double), 1);
+ return Execute(averageSelector, source, lambda);
+ }
+ #endregion Average
+
#region AsEnumerable
#if NET35
///
@@ -1808,13 +1935,74 @@ public static IQueryable SkipWhile([NotNull] this IQueryable source, [NotNull] s
/// Computes the sum of a sequence of numeric values.
///
/// A sequence of numeric values to calculate the sum of.
+ ///
+ ///
+ /// IQueryable queryable = employees.AsQueryable();
+ /// var result1 = queryable.Sum();
+ /// var result2 = queryable.Select("Roles.Sum()");
+ ///
+ ///
/// The sum of the values in the sequence.
+ [PublicAPI]
public static object Sum([NotNull] this IQueryable source)
{
Check.NotNull(source, nameof(source));
- var optimized = OptimizeExpression(Expression.Call(typeof(Queryable), nameof(Queryable.Sum), null, source.Expression));
- return source.Provider.Execute(optimized);
+ var sum = GetMethod(nameof(Queryable.Sum), source.ElementType);
+ return Execute