using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
// ReSharper disable MemberCanBePrivate.Global
namespace FormulaEnginePoC.DynamicExpressionParse
{
///
/// Microsoft provided class. It allows dynamic string based querying.
/// Very handy when, at compile time, you don't know the type of queries that will be generated.
///
internal static class DynamicQueryable
{
#region IQueryable Extensions
public static IQueryable Where(this IQueryable source, string predicate, params object[] values)
{
return (IQueryable)Where((IQueryable)source, predicate, values);
}
public static IQueryable Where(this IQueryable source, string predicate, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
var lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values);
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Where",
new[] { source.ElementType },
source.Expression, Expression.Quote(lambda)));
}
public static IQueryable Select(this IQueryable source, string selector, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (selector == null) throw new ArgumentNullException(nameof(selector));
var lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Select",
new[] { source.ElementType, lambda.Body.Type },
source.Expression, Expression.Quote(lambda)));
}
public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values)
{
return (IQueryable)OrderBy((IQueryable)source, ordering, values);
}
public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (ordering == null) throw new ArgumentNullException(nameof(ordering));
var parameters = new[] {
Expression.Parameter(source.ElementType, "") };
var parser = new ExpressionParser(parameters, ordering, values);
var orderings = parser.ParseOrdering();
var queryExpr = source.Expression;
var methodAsc = "OrderBy";
var methodDesc = "OrderByDescending";
foreach (var o in orderings)
{
queryExpr = Expression.Call(
typeof(Queryable), o.Ascending ? methodAsc : methodDesc,
new[] { source.ElementType, o.Selector.Type },
queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters)));
methodAsc = "ThenBy";
methodDesc = "ThenByDescending";
}
return source.Provider.CreateQuery(queryExpr);
}
public static IQueryable Take(this IQueryable source, int count)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Take",
new[] { source.ElementType },
source.Expression, Expression.Constant(count)));
}
public static IQueryable Skip(this IQueryable source, int count)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Skip",
new[] { source.ElementType },
source.Expression, Expression.Constant(count)));
}
public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector));
var keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values);
var elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values);
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "GroupBy",
new[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda)));
}
public static bool Any(this IQueryable source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return (bool)source.Provider.Execute(
Expression.Call(
typeof(Queryable), "Any",
new[] { source.ElementType }, source.Expression));
}
public static int Count(this IQueryable source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return (int)source.Provider.Execute(
Expression.Call(
typeof(Queryable), "Count",
new[] { source.ElementType }, source.Expression));
}
public static IQueryable Distinct(this IQueryable source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Distinct",
new[] { source.ElementType },
source.Expression));
}
///
/// Dynamically runs an aggregate function on the IQueryable.
///
/// The IQueryable data source.
/// The name of the function to run. Can be Sum, Average, Min, Max.
/// The name of the property to aggregate over.
/// The value of the aggregate function run over the specified property.
public static object Aggregate(this IQueryable source, string function, string member)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (member == null) throw new ArgumentNullException(nameof(member));
// Properties
var property = source.ElementType.GetProperty(member) ?? throw new ArgumentException($"Property [{member}] not found", nameof(member));
var parameter = Expression.Parameter(source.ElementType, "s");
Expression selector = Expression.Lambda(Expression.MakeMemberAccess(parameter, property), parameter);
// We've tried to find an expression of the type Expression>,
// which is expressed as ( (TSource s) => s.Price );
//var methods = typeof(Queryable).GetMethods().Where(x => x.Name == function);
// Method
var aggregateMethod = typeof(Queryable).GetMethods().SingleOrDefault(
m => m.Name == function
&& m.ReturnType == property.PropertyType // should match the type of the property
&& m.IsGenericMethod);
// Sum, Average
if (aggregateMethod != null)
{
return source.Provider.Execute(
Expression.Call(
null,
aggregateMethod.MakeGenericMethod(source.ElementType),
new[] { source.Expression, Expression.Quote(selector) }));
}
// Min, Max
else
{
aggregateMethod = typeof(Queryable).GetMethods().SingleOrDefault(
m => m.Name == function
&& m.GetGenericArguments().Length == 2
&& m.IsGenericMethod);
if (null == aggregateMethod) throw new ArgumentException();
return source.Provider.Execute(
Expression.Call(
null,
aggregateMethod.MakeGenericMethod(source.ElementType, property.PropertyType),
new[] { source.Expression, Expression.Quote(selector) }));
}
}
#endregion IQueryable Extensions
#region IEnumerable Extensions
public static IEnumerable Where(this IEnumerable source, string predicate, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().Where(predicate, values);
}
public static IEnumerable Where(this IEnumerable source, string predicate, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().Where(predicate, values);
}
public static IEnumerable Select(this IEnumerable source, string selector, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().Select(selector, values);
}
public static IEnumerable OrderBy(this IEnumerable source, string ordering, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().OrderBy(ordering, values);
}
public static IEnumerable OrderBy(this IEnumerable source, string ordering, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().OrderBy(ordering, values);
}
public static IEnumerable Take(this IEnumerable source, int count)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().Take(count);
}
public static IEnumerable Skip(this IEnumerable source, int count)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().Skip(count);
}
public static IEnumerable GroupBy(this IEnumerable source, string keySelector, string elementSelector, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().GroupBy(keySelector, elementSelector, values);
}
public static bool Any(this IEnumerable source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().Any();
}
public static int Count(this IEnumerable source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().Count();
}
public static IEnumerable Distinct(this IEnumerable source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return source.AsQueryable().Distinct();
}
#endregion IEnumerable Extensions
}
}