123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- // ReSharper disable MemberCanBePrivate.Global
- namespace FormulaEnginePoC.DynamicExpressionParse
- {
- /// <summary>
- /// 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.
- /// </summary>
- internal static class DynamicQueryable
- {
- #region IQueryable Extensions
- public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
- {
- return (IQueryable<T>)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<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values)
- {
- return (IQueryable<T>)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));
- }
- /// <summary>
- /// Dynamically runs an aggregate function on the IQueryable.
- /// </summary>
- /// <param name="source">The IQueryable data source.</param>
- /// <param name="function">The name of the function to run. Can be Sum, Average, Min, Max.</param>
- /// <param name="member">The name of the property to aggregate over.</param>
- /// <returns>The value of the aggregate function run over the specified property.</returns>
- 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<Func<TSource, TAcc>>,
- // 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<T> Where<T>(this IEnumerable<T> 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<T> OrderBy<T>(this IEnumerable<T> 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
- }
- }
|