From 048e90ba7ec9b9b637cc7004f857569408677405 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Tue, 17 Apr 2018 21:05:18 +0200 Subject: [PATCH 1/2] Support ParsingConfig in all Extension Methods which parse a String --- .../DynamicQueryableExtensions.cs | 353 ++++++++++++++---- .../ExpressionTests.cs | 25 ++ 2 files changed, 313 insertions(+), 65 deletions(-) diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index 3e617426..d8dbd155 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -357,25 +357,36 @@ public static dynamic First([NotNull] this IQueryable source) /// Returns the first element of a sequence that satisfies a specified condition. /// /// The to return the first element 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. /// The first element in source that passes the test in predicate. [PublicAPI] #if NET35 - public static object First([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static object First([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #else - public static dynamic First([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static dynamic First([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #endif { Check.NotNull(source, nameof(source)); Check.NotEmpty(predicate, nameof(predicate)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); return Execute(_firstPredicate, source, lambda); } + /// +#if NET35 + public static object First([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#else + public static dynamic First([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#endif + { + return First(source, null, predicate, args); + } + /// /// Returns the first element of a sequence that satisfies a specified condition. /// @@ -416,25 +427,36 @@ public static dynamic FirstOrDefault([NotNull] this IQueryable source) /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. /// /// The to return the first element 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. /// default if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. [PublicAPI] #if NET35 - public static object FirstOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static object FirstOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #else - public static dynamic FirstOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static dynamic FirstOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #endif { Check.NotNull(source, nameof(source)); Check.NotEmpty(predicate, nameof(predicate)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); return Execute(_firstOrDefaultPredicate, source, lambda); } + /// +#if NET35 + public static object FirstOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#else + public static dynamic FirstOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#endif + { + return FirstOrDefault(source, null, predicate, args); + } + /// /// Returns the first element of a sequence that satisfies a specified condition or a default value if no such element is found. /// @@ -460,6 +482,7 @@ public static dynamic FirstOrDefault([NotNull] this IQueryable source, [NotNull] /// and creates a result value from each group and its key. /// /// A whose elements to group. + /// The . /// A string expression to specify the key for each element. /// A string expression to specify a result value from each group. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. @@ -471,15 +494,15 @@ public static dynamic FirstOrDefault([NotNull] this IQueryable source, [NotNull] /// /// [PublicAPI] - public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] string keySelector, [NotNull] string resultSelector, object[] args) + public static IQueryable GroupBy([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string keySelector, [NotNull] string resultSelector, object[] args) { Check.NotNull(source, nameof(source)); Check.NotEmpty(keySelector, nameof(keySelector)); Check.NotEmpty(resultSelector, nameof(resultSelector)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression keyLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, keySelector, args); - LambdaExpression elementLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, resultSelector, args); + LambdaExpression keyLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, keySelector, args); + LambdaExpression elementLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, resultSelector, args); var optimized = OptimizeExpression(Expression.Call( typeof(Queryable), nameof(Queryable.GroupBy), @@ -489,11 +512,19 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] str return source.Provider.CreateQuery(optimized); } + /// + [PublicAPI] + public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] string keySelector, [NotNull] string resultSelector, object[] args) + { + return GroupBy(source, null, keySelector, resultSelector, args); + } + /// /// Groups the elements of a sequence according to a specified key string function /// and creates a result value from each group and its key. /// /// A whose elements to group. + /// The . /// A string expression to specify the key for each element. /// A string expression to specify a result value from each group. /// A where each element represents a projection over a group and its key. @@ -503,13 +534,19 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] str /// var groupResult2 = queryable.GroupBy("new (NumberPropertyAsKey, StringPropertyAsKey)", "new (StringProperty1, StringProperty2)"); /// /// - public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] string keySelector, [NotNull] string resultSelector) + public static IQueryable GroupBy([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string keySelector, [NotNull] string resultSelector) { Check.NotNull(source, nameof(source)); Check.NotEmpty(keySelector, nameof(keySelector)); Check.NotEmpty(resultSelector, nameof(resultSelector)); - return GroupBy(source, keySelector, resultSelector, null); + return GroupBy(source, config, keySelector, resultSelector, null); + } + + /// + public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] string keySelector, [NotNull] string resultSelector) + { + return GroupBy(source, null, keySelector, resultSelector); } /// @@ -517,6 +554,7 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] str /// and creates a result value from each group and its key. /// /// A whose elements to group. + /// The . /// A string expression to specify the key for 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 where each element represents a projection over a group and its key. @@ -527,13 +565,13 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] str /// /// [PublicAPI] - public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] string keySelector, [CanBeNull] params object[] args) + public static IQueryable GroupBy([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string keySelector, [CanBeNull] params object[] args) { Check.NotNull(source, nameof(source)); Check.NotEmpty(keySelector, nameof(keySelector)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression keyLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, keySelector, args); + LambdaExpression keyLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, keySelector, args); var optimized = OptimizeExpression(Expression.Call( typeof(Queryable), nameof(Queryable.GroupBy), @@ -541,6 +579,14 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] str return source.Provider.CreateQuery(optimized); } + + /// + [PublicAPI] + public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] string keySelector, [CanBeNull] params object[] args) + { + return GroupBy(source, (ParsingConfig)null, keySelector, args); + } + #endregion GroupBy #region GroupByMany @@ -550,9 +596,10 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] str /// /// /// A whose elements to group. + /// The . /// expressions to specify the keys for each element. /// A of type where each element represents a projection over a group, its key, and its subgroups. - public static IEnumerable GroupByMany([NotNull] this IEnumerable source, params string[] keySelectors) + public static IEnumerable GroupByMany([NotNull] this IEnumerable source, [CanBeNull] ParsingConfig config, params string[] keySelectors) { Check.NotNull(source, nameof(source)); Check.HasNoNulls(keySelectors, nameof(keySelectors)); @@ -562,13 +609,19 @@ public static IEnumerable GroupByMany([NotNull] this IEnu bool createParameterCtor = true; foreach (var selector in keySelectors) { - LambdaExpression l = DynamicExpressionParser.ParseLambda(createParameterCtor, typeof(TElement), typeof(object), selector); + LambdaExpression l = DynamicExpressionParser.ParseLambda(config, createParameterCtor, typeof(TElement), typeof(object), selector); selectors.Add((Func)l.Compile()); } return GroupByManyInternal(source, selectors.ToArray(), 0); } + /// + public static IEnumerable GroupByMany([NotNull] this IEnumerable source, params string[] keySelectors) + { + return GroupByMany(source, null, keySelectors); + } + /// /// Groups the elements of a sequence according to multiple specified key functions /// and creates a result value from each group (and subgroups) and its key. @@ -609,13 +662,14 @@ static IEnumerable GroupByManyInternal(IEnumerable /// The first sequence to join. + /// The . /// The sequence to join to the first sequence. /// A dynamic function to extract the join key from each element of the first sequence. /// A dynamic function to extract the join key from each element of the second sequence. /// A dynamic function to create a result element from an element from the first sequence and a collection of matching elements from the second sequence. /// An object array that contains zero or more objects to insert into the predicates as parameters. Similar to the way String.Format formats strings. /// An obtained by performing a grouped join on two sequences. - public static IQueryable GroupJoin([NotNull] this IQueryable outer, [NotNull] IEnumerable inner, [NotNull] string outerKeySelector, [NotNull] string innerKeySelector, [NotNull] string resultSelector, params object[] args) + public static IQueryable GroupJoin([NotNull] this IQueryable outer, [CanBeNull] ParsingConfig config, [NotNull] IEnumerable inner, [NotNull] string outerKeySelector, [NotNull] string innerKeySelector, [NotNull] string resultSelector, params object[] args) { Check.NotNull(outer, nameof(outer)); Check.NotNull(inner, nameof(inner)); @@ -627,8 +681,8 @@ public static IQueryable GroupJoin([NotNull] this IQueryable outer, [NotNull] IE Type innerType = inner.AsQueryable().ElementType; bool createParameterCtor = outer.IsLinqToObjects(); - LambdaExpression outerSelectorLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, outerType, null, outerKeySelector, args); - LambdaExpression innerSelectorLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, innerType, null, innerKeySelector, args); + LambdaExpression outerSelectorLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, outerType, null, outerKeySelector, args); + LambdaExpression innerSelectorLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, innerType, null, innerKeySelector, args); CheckOuterAndInnerTypes(createParameterCtor, outerType, innerType, outerKeySelector, innerKeySelector, ref outerSelectorLambda, ref innerSelectorLambda, args); @@ -638,7 +692,7 @@ public static IQueryable GroupJoin([NotNull] this IQueryable outer, [NotNull] IE ParameterExpressionHelper.CreateParameterExpression(typeof(IEnumerable<>).MakeGenericType(innerType), "inner") }; - LambdaExpression resultSelectorLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, parameters, null, resultSelector, args); + LambdaExpression resultSelectorLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, parameters, null, resultSelector, args); return outer.Provider.CreateQuery(Expression.Call( typeof(Queryable), nameof(Queryable.GroupJoin), @@ -649,6 +703,13 @@ public static IQueryable GroupJoin([NotNull] this IQueryable outer, [NotNull] IE Expression.Quote(innerSelectorLambda), Expression.Quote(resultSelectorLambda))); } + + /// + public static IQueryable GroupJoin([NotNull] this IQueryable outer, [NotNull] IEnumerable inner, [NotNull] string outerKeySelector, [NotNull] string innerKeySelector, [NotNull] string resultSelector, params object[] args) + { + return GroupJoin(outer, null, inner, outerKeySelector, innerKeySelector, resultSelector, args); + } + #endregion #region Join @@ -656,13 +717,14 @@ public static IQueryable GroupJoin([NotNull] this IQueryable outer, [NotNull] IE /// Correlates the elements of two sequences based on matching keys. The default equality comparer is used to compare keys. /// /// The first sequence to join. + /// The . /// The sequence to join to the first sequence. /// A dynamic function to extract the join key from each element of the first sequence. /// A dynamic function to extract the join key from each element of the second sequence. /// A dynamic function to create a result element from two matching elements. /// An object array that contains zero or more objects to insert into the predicates as parameters. Similar to the way String.Format formats strings. /// An obtained by performing an inner join on two sequences. - public static IQueryable Join([NotNull] this IQueryable outer, [NotNull] IEnumerable inner, [NotNull] string outerKeySelector, [NotNull] string innerKeySelector, [NotNull] string resultSelector, params object[] args) + public static IQueryable Join([NotNull] this IQueryable outer, [CanBeNull] ParsingConfig config, [NotNull] IEnumerable inner, [NotNull] string outerKeySelector, [NotNull] string innerKeySelector, [NotNull] string resultSelector, params object[] args) { //http://stackoverflow.com/questions/389094/how-to-create-a-dynamic-linq-join-extension-method @@ -676,8 +738,8 @@ public static IQueryable Join([NotNull] this IQueryable outer, [NotNull] IEnumer Type innerType = inner.AsQueryable().ElementType; bool createParameterCtor = outer.IsLinqToObjects(); - LambdaExpression outerSelectorLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, outerType, null, outerKeySelector, args); - LambdaExpression innerSelectorLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, innerType, null, innerKeySelector, args); + LambdaExpression outerSelectorLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, outerType, null, outerKeySelector, args); + LambdaExpression innerSelectorLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, innerType, null, innerKeySelector, args); CheckOuterAndInnerTypes(createParameterCtor, outerType, innerType, outerKeySelector, innerKeySelector, ref outerSelectorLambda, ref innerSelectorLambda, args); @@ -687,7 +749,7 @@ public static IQueryable Join([NotNull] this IQueryable outer, [NotNull] IEnumer ParameterExpressionHelper.CreateParameterExpression(innerType, "inner") }; - LambdaExpression resultSelectorLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, parameters, null, resultSelector, args); + LambdaExpression resultSelectorLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, parameters, null, resultSelector, args); var optimized = OptimizeExpression(Expression.Call( typeof(Queryable), "Join", @@ -702,11 +764,18 @@ public static IQueryable Join([NotNull] this IQueryable outer, [NotNull] IEnumer return outer.Provider.CreateQuery(optimized); } + /// + public static IQueryable Join([NotNull] this IQueryable outer, [NotNull] IEnumerable inner, [NotNull] string outerKeySelector, [NotNull] string innerKeySelector, [NotNull] string resultSelector, params object[] args) + { + return Join(outer, null, inner, outerKeySelector, innerKeySelector, resultSelector, args); + } + /// /// Correlates the elements of two sequences based on matching keys. The default equality comparer is used to compare keys. /// /// The type of the elements of both sequences, and the result. /// The first sequence to join. + /// The . /// The sequence to join to the first sequence. /// A dynamic function to extract the join key from each element of the first sequence. /// A dynamic function to extract the join key from each element of the second sequence. @@ -714,10 +783,17 @@ public static IQueryable Join([NotNull] this IQueryable outer, [NotNull] IEnumer /// An object array that contains zero or more objects to insert into the predicates as parameters. Similar to the way String.Format formats strings. /// This overload only works on elements where both sequences and the resulting element match. /// An that has elements of type TResult obtained by performing an inner join on two sequences. + public static IQueryable Join([NotNull] this IQueryable outer, [CanBeNull] ParsingConfig config, [NotNull] IEnumerable inner, [NotNull] string outerKeySelector, [NotNull] string innerKeySelector, string resultSelector, params object[] args) + { + return (IQueryable)Join((IQueryable)outer, config, (IEnumerable)inner, outerKeySelector, innerKeySelector, resultSelector, args); + } + + /// public static IQueryable Join([NotNull] this IQueryable outer, [NotNull] IEnumerable inner, [NotNull] string outerKeySelector, [NotNull] string innerKeySelector, string resultSelector, params object[] args) { - return (IQueryable)Join((IQueryable)outer, (IEnumerable)inner, outerKeySelector, innerKeySelector, resultSelector, args); + return Join(outer, null, inner, outerKeySelector, innerKeySelector, resultSelector, args); } + #endregion Join #region Last @@ -744,24 +820,36 @@ public static dynamic Last([NotNull] this IQueryable source) /// Returns the last element of a sequence that satisfies a specified condition. /// /// The to return the last element 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. /// The first element in source that passes the test in predicate. #if NET35 - public static object Last([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static object Last([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #else - public static dynamic Last([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static dynamic Last([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #endif { Check.NotNull(source, nameof(source)); Check.NotEmpty(predicate, nameof(predicate)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); return Execute(_lastPredicate, source, lambda); } + /// +#if NET35 + public static object Last([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#else + public static dynamic Last([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#endif + { + return Last(source, null, predicate, args); + } + + /// /// Returns the last element of a sequence that satisfies a specified condition. /// @@ -803,24 +891,35 @@ public static dynamic LastOrDefault([NotNull] this IQueryable source) /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. /// /// The to return the last element 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. /// The first element in source that passes the test in predicate. #if NET35 - public static object LastOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static object LastOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #else - public static dynamic LastOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static dynamic LastOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #endif { Check.NotNull(source, nameof(source)); Check.NotEmpty(predicate, nameof(predicate)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); return Execute(_lastDefaultPredicate, source, lambda); } + /// +#if NET35 + public static object LastOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#else + public static dynamic LastOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#endif + { + return LastOrDefault(source, null, predicate, args); + } + /// /// Returns the last element of a sequence that satisfies a specified condition, or a default value if the sequence contains no elements. /// @@ -844,6 +943,7 @@ public static dynamic LastOrDefault([NotNull] this IQueryable source, [NotNull] /// /// The type of the elements of source. /// A sequence of values to order. + /// The . /// An expression string to indicate values to order by. /// 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 whose elements are sorted according to the specified . @@ -856,15 +956,22 @@ public static dynamic LastOrDefault([NotNull] this IQueryable source, [NotNull] /// ]]> /// /// - public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [NotNull] string ordering, params object[] args) + public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string ordering, params object[] args) { return (IOrderedQueryable)OrderBy((IQueryable)source, ordering, args); } + /// + public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [NotNull] string ordering, params object[] args) + { + return OrderBy(source, (ParsingConfig)null, ordering, args); + } + /// /// Sorts the elements of a sequence in ascending or descending order according to a key. /// /// A sequence of values to order. + /// The . /// An expression string to indicate values to order by. /// 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 whose elements are sorted according to the specified . @@ -875,13 +982,13 @@ public static IOrderedQueryable OrderBy([NotNull] this IQuerya /// var resultMultiple = queryable.OrderBy("NumberProperty, StringProperty DESC"); /// /// - public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [NotNull] string ordering, params object[] args) + public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string ordering, params object[] args) { Check.NotNull(source, nameof(source)); Check.NotEmpty(ordering, nameof(ordering)); ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty) }; - ExpressionParser parser = new ExpressionParser(parameters, ordering, args, null); + ExpressionParser parser = new ExpressionParser(parameters, ordering, args, config); IList dynamicOrderings = parser.ParseOrdering(); Expression queryExpr = source.Expression; @@ -897,6 +1004,13 @@ public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [NotNu var optimized = OptimizeExpression(queryExpr); return (IOrderedQueryable)source.Provider.CreateQuery(optimized); } + + /// + public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [NotNull] string ordering, params object[] args) + { + return OrderBy(source, null, ordering, args); + } + #endregion OrderBy #region Page/PageResult @@ -1006,6 +1120,7 @@ public static IQueryable Reverse([NotNull] this IQueryable source) /// Projects each element of a sequence into a new form. /// /// A sequence of values to project. + /// The . /// A projection string expression 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. /// An whose elements are the result of invoking a projection string on each element of source. @@ -1015,13 +1130,13 @@ public static IQueryable Reverse([NotNull] this IQueryable source) /// var dynamicObject = queryable.Select("new (StringProperty1, StringProperty2 as OtherStringPropertyName)"); /// /// - public static IQueryable Select([NotNull] this IQueryable source, [NotNull] string selector, params object[] args) + public static IQueryable Select([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string selector, params object[] args) { Check.NotNull(source, nameof(source)); Check.NotEmpty(selector, nameof(selector)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, selector, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, selector, args); var optimized = OptimizeExpression(Expression.Call( typeof(Queryable), nameof(Queryable.Select), @@ -1032,12 +1147,19 @@ public static IQueryable Select([NotNull] this IQueryable source, [NotNull] stri return source.Provider.CreateQuery(optimized); } + /// + public static IQueryable Select([NotNull] this IQueryable source, [NotNull] string selector, params object[] args) + { + return Select(source, (ParsingConfig)null, selector, args); + } + /// /// Projects each element of a sequence into a new class of type TResult. /// Details see . /// /// The type of the result. /// A sequence of values to project. + /// The . /// A projection string expression to apply to each element. /// An object array that contains zero or more objects to insert into the predicate as parameters. /// An whose elements are the result of invoking a projection string on each element of source. @@ -1048,13 +1170,13 @@ public static IQueryable Select([NotNull] this IQueryable source, [NotNull] stri /// ]]> /// /// - public static IQueryable Select([NotNull] this IQueryable source, [NotNull] string selector, params object[] args) + public static IQueryable Select([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string selector, params object[] args) { Check.NotNull(source, nameof(source)); Check.NotEmpty(selector, nameof(selector)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, typeof(TResult), selector, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, typeof(TResult), selector, args); var optimized = OptimizeExpression(Expression.Call( typeof(Queryable), nameof(Queryable.Select), @@ -1064,11 +1186,18 @@ public static IQueryable Select([NotNull] this IQueryable sour return source.Provider.CreateQuery(optimized); } + /// + public static IQueryable Select([NotNull] this IQueryable source, [NotNull] string selector, params object[] args) + { + return Select(source, null, selector, args); + } + /// /// Projects each element of a sequence into a new class of type TResult. /// Details see http://solutionizing.net/category/linq/ /// /// A sequence of values to project. + /// The . /// The result type. /// A projection string expression to apply to each element. /// An object array that contains zero or more objects to insert into the predicate as parameters. @@ -1078,14 +1207,14 @@ public static IQueryable Select([NotNull] this IQueryable sour /// var users = queryable.Select(typeof(User), "new (Username, Pwd as Password)"); /// /// - public static IQueryable Select([NotNull] this IQueryable source, [NotNull] Type resultType, [NotNull] string selector, params object[] args) + public static IQueryable Select([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] Type resultType, [NotNull] string selector, params object[] args) { Check.NotNull(source, nameof(source)); Check.NotNull(resultType, nameof(resultType)); Check.NotEmpty(selector, nameof(selector)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, resultType, selector, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, resultType, selector, args); var optimized = OptimizeExpression(Expression.Call( typeof(Queryable), nameof(Queryable.Select), @@ -1094,6 +1223,13 @@ public static IQueryable Select([NotNull] this IQueryable source, [NotNull] Type return source.Provider.CreateQuery(optimized); } + + /// + public static IQueryable Select([NotNull] this IQueryable source, [NotNull] Type resultType, [NotNull] string selector, params object[] args) + { + return Select(source, null, resultType, selector, args); + } + #endregion Select #region SelectMany @@ -1101,6 +1237,7 @@ public static IQueryable Select([NotNull] this IQueryable source, [NotNull] Type /// Projects each element of a sequence to an and combines the resulting sequences into one sequence. /// /// A sequence of values to project. + /// The . /// A projection string expression 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. /// An whose elements are the result of invoking a one-to-many projection function on each element of the input sequence. @@ -1109,18 +1246,25 @@ public static IQueryable Select([NotNull] this IQueryable source, [NotNull] Type /// var roles = users.SelectMany("Roles"); /// /// - public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] string selector, params object[] args) + public static IQueryable SelectMany([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string selector, params object[] args) { Check.NotNull(source, nameof(source)); Check.NotEmpty(selector, nameof(selector)); - return SelectManyInternal(source, null, selector, args); + return SelectManyInternal(source, config, null, selector, args); + } + + /// + public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] string selector, params object[] args) + { + return SelectMany(source, (ParsingConfig)null, selector, args); } /// /// Projects each element of a sequence to an and combines the resulting sequences into one sequence. /// /// A sequence of values to project. + /// The . /// A projection string expression to apply to each element. /// The result type. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. @@ -1130,19 +1274,25 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] /// var permissions = users.SelectMany(typeof(Permission), "Roles.SelectMany(Permissions)"); /// /// - public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] Type resultType, [NotNull] string selector, params object[] args) + public static IQueryable SelectMany([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] Type resultType, [NotNull] string selector, params object[] args) { Check.NotNull(source, nameof(source)); Check.NotNull(resultType, nameof(resultType)); Check.NotEmpty(selector, nameof(selector)); - return SelectManyInternal(source, resultType, selector, args); + return SelectManyInternal(source, config, resultType, selector, args); } - private static IQueryable SelectManyInternal(IQueryable source, Type resultType, string selector, params object[] args) + /// + public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] Type resultType, [NotNull] string selector, params object[] args) + { + return SelectMany(source, null, resultType, selector, args); + } + + private static IQueryable SelectManyInternal(IQueryable source, ParsingConfig config, Type resultType, string selector, params object[] args) { bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, selector, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, selector, args); //Extra help to get SelectMany to work from StackOverflow Answer //http://stackoverflow.com/a/3001674/2465182 @@ -1182,6 +1332,7 @@ private static IQueryable SelectManyInternal(IQueryable source, Type resultType, /// /// The type of the result. /// A sequence of values to project. + /// The . /// A projection string expression 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. /// An whose elements are the result of invoking a one-to-many projection function on each element of the input sequence. @@ -1192,7 +1343,7 @@ private static IQueryable SelectManyInternal(IQueryable source, Type resultType, /// ]]> /// /// - public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] string selector, params object[] args) + public static IQueryable SelectMany([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string selector, params object[] args) { Check.NotNull(source, nameof(source)); Check.NotEmpty(selector, nameof(selector)); @@ -1215,6 +1366,12 @@ public static IQueryable SelectMany([NotNull] this IQueryable return source.Provider.CreateQuery(optimized); } + /// + public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] string selector, params object[] args) + { + return SelectMany(source, null, selector, args); + } + /// /// Projects each element of a sequence to an /// and invokes a result selector function on each element therein. The resulting @@ -1222,6 +1379,7 @@ public static IQueryable SelectMany([NotNull] this IQueryable /// sequence and returned. /// /// A sequence of values to project. + /// The . /// A projection function to apply to each element of the input sequence. /// A projection function to apply to each element of each intermediate sequence. Should only use x and y as parameter names. /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings. @@ -1238,11 +1396,17 @@ public static IQueryable SelectMany([NotNull] this IQueryable /// ]]> /// /// - public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] string collectionSelector, [NotNull] string resultSelector, [CanBeNull] object[] collectionSelectorArgs = null, [CanBeNull] params object[] resultSelectorArgs) + public static IQueryable SelectMany([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string collectionSelector, [NotNull] string resultSelector, [CanBeNull] object[] collectionSelectorArgs = null, [CanBeNull] params object[] resultSelectorArgs) { return SelectMany(source, collectionSelector, resultSelector, "x", "y", collectionSelectorArgs, resultSelectorArgs); } + /// + public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] string collectionSelector, [NotNull] string resultSelector, [CanBeNull] object[] collectionSelectorArgs = null, [CanBeNull] params object[] resultSelectorArgs) + { + return SelectMany(source, (ParsingConfig)null, collectionSelector, resultSelector, "x", "y", collectionSelectorArgs, resultSelectorArgs); + } + /// /// Projects each element of a sequence to an /// and invokes a result selector function on each element therein. The resulting @@ -1250,6 +1414,7 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] /// sequence and returned. /// /// A sequence of values to project. + /// The . /// A projection function to apply to each element of the input sequence. /// The name from collectionParameter to use. Default is x. /// A projection function to apply to each element of each intermediate sequence. @@ -1268,8 +1433,7 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] /// ]]> /// /// - public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] string collectionSelector, [NotNull] string resultSelector, - [NotNull] string collectionParameterName, [NotNull] string resultParameterName, [CanBeNull] object[] collectionSelectorArgs = null, [CanBeNull] params object[] resultSelectorArgs) + public static IQueryable SelectMany([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string collectionSelector, [NotNull] string resultSelector, [NotNull] string collectionParameterName, [NotNull] string resultParameterName, [CanBeNull] object[] collectionSelectorArgs = null, [CanBeNull] params object[] resultSelectorArgs) { Check.NotNull(source, nameof(source)); Check.NotEmpty(collectionSelector, nameof(collectionSelector)); @@ -1278,7 +1442,7 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] Check.NotEmpty(resultParameterName, nameof(resultParameterName)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression sourceSelectLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, collectionSelector, collectionSelectorArgs); + LambdaExpression sourceSelectLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, collectionSelector, collectionSelectorArgs); //we have to adjust to lambda to return an IEnumerable instead of whatever the actual property is. Type sourceLambdaInputType = source.Expression.Type.GetGenericArguments()[0]; @@ -1292,7 +1456,7 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] ParameterExpression xParameter = ParameterExpressionHelper.CreateParameterExpression(source.ElementType, collectionParameterName); ParameterExpression yParameter = ParameterExpressionHelper.CreateParameterExpression(sourceLambdaResultType, resultParameterName); - LambdaExpression resultSelectLambda = DynamicExpressionParser.ParseLambda(createParameterCtor, new[] { xParameter, yParameter }, null, resultSelector, resultSelectorArgs); + LambdaExpression resultSelectLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, new[] { xParameter, yParameter }, null, resultSelector, resultSelectorArgs); Type resultLambdaResultType = resultSelectLambda.Body.Type; var optimized = OptimizeExpression(Expression.Call( @@ -1303,6 +1467,13 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] return source.Provider.CreateQuery(optimized); } + + /// + public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] string collectionSelector, [NotNull] string resultSelector, [NotNull] string collectionParameterName, [NotNull] string resultParameterName, [CanBeNull] object[] collectionSelectorArgs = null, [CanBeNull] params object[] resultSelectorArgs) + { + return SelectMany(source, null, collectionSelector, resultSelector, collectionParameterName, resultParameterName, collectionSelectorArgs, resultSelectorArgs); + } + #endregion SelectMany #region Single/SingleOrDefault @@ -1331,24 +1502,35 @@ public static dynamic Single([NotNull] this IQueryable source) /// is not exactly one element in the sequence. /// /// The to return the last element 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. /// The first element in source that passes the test in predicate. #if NET35 - public static object Single([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static object Single([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #else - public static dynamic Single([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static dynamic Single([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #endif { Check.NotNull(source, nameof(source)); Check.NotEmpty(predicate, nameof(predicate)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); return Execute(_singlePredicate, source, lambda); } + /// +#if NET35 + public static object Single([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#else + public static dynamic Single([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#endif + { + return Single(source, null, predicate, args); + } + /// /// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there /// is not exactly one element in the sequence. @@ -1392,24 +1574,35 @@ public static dynamic SingleOrDefault([NotNull] this IQueryable source) /// is empty; and throws an exception if there is not exactly one element in the sequence. /// /// The to return the last element 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. /// The first element in source that passes the test in predicate. #if NET35 - public static object SingleOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static object SingleOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #else - public static dynamic SingleOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) + public static dynamic SingleOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #endif { Check.NotNull(source, nameof(source)); Check.NotEmpty(predicate, nameof(predicate)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); return Execute(_singleDefaultPredicate, source, lambda); } + /// +#if NET35 + public static object SingleOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#else + public static dynamic SingleOrDefault([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args) +#endif + { + return SingleOrDefault(source, null, predicate, args); + } + /// /// Returns the only element of a sequence that satisfies a specified condition or a default value if the sequence /// is empty; and throws an exception if there is not exactly one element in the sequence. @@ -1457,6 +1650,7 @@ public static IQueryable Skip([NotNull] this IQueryable source, int count) /// Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. /// /// A sequence to check for being empty. + /// 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. /// @@ -1467,16 +1661,23 @@ public static IQueryable Skip([NotNull] this IQueryable source, int count) /// /// /// An that contains elements from source starting at the first element in the linear series that does not pass the test specified by predicate. - public static IQueryable SkipWhile([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args) + public static IQueryable SkipWhile([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, [CanBeNull] params object[] args) { Check.NotNull(source, nameof(source)); Check.NotNull(predicate, nameof(predicate)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); return CreateQuery(_skipWhilePredicate, source, lambda); } + + /// + public static IQueryable SkipWhile([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args) + { + return SkipWhile(source, null, predicate, args); + } + #endregion SkipWhile #region Sum @@ -1518,6 +1719,7 @@ public static IQueryable Take([NotNull] this IQueryable source, int count) /// Returns elements from a sequence as long as a specified condition is true. /// /// A sequence to check for being empty. + /// 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. /// @@ -1528,16 +1730,23 @@ public static IQueryable Take([NotNull] this IQueryable source, int count) /// /// /// An that contains elements from the input sequence occurring before the element at which the test specified by predicate no longer passes. - public static IQueryable TakeWhile([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args) + public static IQueryable TakeWhile([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, [CanBeNull] params object[] args) { Check.NotNull(source, nameof(source)); Check.NotNull(predicate, nameof(predicate)); bool createParameterCtor = source.IsLinqToObjects(); - LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args); + LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args); return CreateQuery(_takeWhilePredicate, source, lambda); } + + /// + public static IQueryable TakeWhile([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args) + { + return TakeWhile(source, null, predicate, args); + } + #endregion TakeWhile #region ThenBy @@ -1546,6 +1755,7 @@ public static IQueryable TakeWhile([NotNull] this IQueryable source, [NotNull] s /// /// The type of the elements of source. /// A sequence of values to order. + /// The . /// An expression string to indicate values to order by. /// 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 whose elements are sorted according to the specified . @@ -1559,15 +1769,21 @@ public static IQueryable TakeWhile([NotNull] this IQueryable source, [NotNull] s /// ]]> /// /// - public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source, [NotNull] string ordering, params object[] args) + public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string ordering, params object[] args) { - return (IOrderedQueryable)ThenBy((IOrderedQueryable)source, ordering, args); + return (IOrderedQueryable)ThenBy((IOrderedQueryable)source, config, ordering, args); } + /// + public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source, [NotNull] string ordering, params object[] args) + { + return (IOrderedQueryable)ThenBy(source, (ParsingConfig)null, ordering, args); + } /// /// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. /// /// A sequence of values to order. + /// The . /// An expression string to indicate values to order by. /// 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 whose elements are sorted according to the specified . @@ -1579,13 +1795,13 @@ public static IOrderedQueryable ThenBy([NotNull] this IOrdered /// var resultMultiple = result.OrderBy("NumberProperty, StringProperty DESC"); /// /// - public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source, [NotNull] string ordering, params object[] args) + public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string ordering, params object[] args) { Check.NotNull(source, nameof(source)); Check.NotEmpty(ordering, nameof(ordering)); ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty) }; - ExpressionParser parser = new ExpressionParser(parameters, ordering, args, null); + ExpressionParser parser = new ExpressionParser(parameters, ordering, args, config); IList dynamicOrderings = parser.ParseOrdering(forceThenBy: true); Expression queryExpr = source.Expression; @@ -1601,6 +1817,13 @@ public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source, var optimized = OptimizeExpression(queryExpr); return (IOrderedQueryable)source.Provider.CreateQuery(optimized); } + + /// + public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source, [NotNull] string ordering, params object[] args) + { + return ThenBy(source, null, ordering, args); + } + #endregion OrderBy #region Where diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs index 154c66fe..af22335b 100644 --- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs @@ -578,6 +578,31 @@ public void ExpressionTests_Enum() Check.That(resultEqualStringMixedCaseParamRight.Single()).Equals(TestEnum.Var5); } + [Fact] + public void ExpressionTests_ConfigExtensions() + { + var config = new ParsingConfig(); +#if NETSTANDARD + config.CustomTypeProvider = new NetStandardCustomTypeProvider(); +#endif + + // Arrange + var lst = new List { TestEnum.Var1, TestEnum.Var2, TestEnum.Var3, TestEnum.Var4, TestEnum.Var5, TestEnum.Var6 }; + var qry = lst.AsQueryable(); + + // Act + var r1 = qry.Any(config, "it < TestEnum.Var4"); + var r2 = qry.First(config, "TestEnum.Var4 > it"); + var r3 = qry.FirstOrDefault(config, "it = Var5"); + var r4 = qry.Last(config, "@0 = it", TestEnum.Var5); + var r5 = qry.LastOrDefault("@0 = it", 8); + var r6 = qry.Single(config, "it = @0", "Var5"); + var r7 = qry.SingleOrDefault(config, "@0 = it", "vAR5"); + var r8 = qry.SkipWhile(config, "it < TestEnum.Var4").FirstOrDefault(); + var r9 = qry.TakeWhile(config, "it < TestEnum.Var4").FirstOrDefault(); + var r10 = qry.GroupBy(config, "it").FirstOrDefault(); + } + [Fact] public void ExpressionTests_Enum_Nullable() { From 7e9b01acaf10123a027fb89f45812e44ccd3f5dc Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 6 May 2018 11:16:56 +0200 Subject: [PATCH 2/2] Update versions --- ChangeLog.txt | 2 +- .../EntityFramework.DynamicLinq.csproj | 2 +- .../Microsoft.EntityFrameworkCore.DynamicLinq.csproj | 2 +- src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index ffcfe2c5..ac3a8c49 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,5 +1,5 @@ https://github.com/GitTools/GitReleaseNotes -GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.8.6 +GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.8.7 GitReleaseNotes.exe . /OutputFile CHANGELOG.md /allTags diff --git a/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj b/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj index 80bea81c..cbb3ea0e 100644 --- a/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj +++ b/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj @@ -2,7 +2,7 @@ Dynamic Linq extensions for EntityFramework which adds Async support EntityFramework.DynamicLinq - 1.0.8.6 + 1.0.8.7 Stef Heyenrath net45;net46 EF diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj index 3b64420e..237e2186 100644 --- a/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj +++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj @@ -2,7 +2,7 @@ Dynamic Linq extensions for Microsoft.EntityFrameworkCore which adds Async support Microsoft.EntityFrameworkCore.DynamicLinq - 1.0.8.6 + 1.0.8.7 Stef Heyenrath net451;net46;netstandard1.3;netstandard2.0;uap10.0 $(DefineConstants);EFCORE diff --git a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj index a0388560..3468e6c3 100644 --- a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj +++ b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj @@ -2,7 +2,7 @@ This is a .NETStandard/ .NET Core port of the the Microsoft assembly for the .Net 4.0 Dynamic language functionality. System.Linq.Dynamic.Core - 1.0.8.6 + 1.0.8.7 Microsoft;Scott Guthrie;King Wilder;Nathan Arnott;Stef Heyenrath net35;net40;net45;net46;uap10.0;netstandard1.3;netstandard2.0 true