diff --git a/src/System.Linq.Dynamic.Core/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/ExpressionParser.cs index c0014f18..ae7c3b54 100644 --- a/src/System.Linq.Dynamic.Core/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/ExpressionParser.cs @@ -145,7 +145,8 @@ interface IEnumerableSignatures void Distinct(); void First(bool predicate); void FirstOrDefault(bool predicate); - void GroupBy(object selector); + void GroupBy(object keySelector); + void GroupBy(object keySelector, object elementSelector); void Last(bool predicate); void LastOrDefault(bool predicate); void Max(object selector); @@ -181,6 +182,7 @@ interface IEnumerableSignatures void LastOrDefault(); void Single(); void SingleOrDefault(); + void ToList(); } // These shorthands have different name than actual type and therefore not recognized by default from the _predefinedTypes @@ -1641,7 +1643,14 @@ Expression ParseAggregate(Expression instance, Type elementType, string methodNa Type[] typeArgs; if (new[] { "Min", "Max", "Select", "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending", "GroupBy" }.Contains(signature.Name)) { - typeArgs = new[] { elementType, args[0].Type }; + if (args.Length == 2) + { + typeArgs = new[] { elementType, args[0].Type, args[1].Type }; + } + else + { + typeArgs = new[] { elementType, args[0].Type }; + } } else if (signature.Name == "SelectMany") { @@ -1668,7 +1677,14 @@ Expression ParseAggregate(Expression instance, Type elementType, string methodNa } else { - args = new[] { instance, Expression.Lambda(args[0], innerIt) }; + if (args.Length == 2) + { + args = new[] { instance, Expression.Lambda(args[0], innerIt), Expression.Lambda(args[1], innerIt) }; + } + else + { + args = new[] {instance, Expression.Lambda(args[0], innerIt)}; + } } } diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 23812ce4..5c8c4db5 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -53,6 +53,48 @@ public void ParseLambda_EmptyParameterList() Check.That(result).Equals(3); } + [Fact] + public void ParseLambdaComplex_1() + { + // Arrange + var testList = User.GenerateSampleModels(51); + var qry = testList.AsQueryable(); + + // Act + string query = "GroupBy(x => new { x.Profile.Age }, it).OrderBy(gg => gg.Key.Age).Select(j => new (j.Key.Age, j.Sum(k => k.Income) As TotalIncome))"; + LambdaExpression expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); + Delegate del = expression.Compile(); + IEnumerable result = del.DynamicInvoke(qry) as IEnumerable; + + var expected = qry.GroupBy(x => new { x.Profile.Age }, x => x).OrderBy(gg => gg.Key.Age).Select(j => new { j.Key.Age, TotalIncome = j.Sum(k => k.Income) }).Select(c => new ComplexParseLambda1Result { Age = c.Age, TotalIncome = c.TotalIncome }).Cast().ToArray(); + + // Assert + Check.That(result).IsNotNull(); + Check.That(result).HasSize(expected.Length); + Check.That(result.ToArray()[0]).Equals(expected[0]); + } + + [Fact] + public void ParseLambdaComplex_2() + { + // Arrange + var testList = User.GenerateSampleModels(51); + var qry = testList.AsQueryable(); + + // Act + string query = "OrderBy(gg => gg.Income).ToList()"; + LambdaExpression expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); + Delegate del = expression.Compile(); + IEnumerable result = del.DynamicInvoke(qry) as IEnumerable; + + var expected = qry.OrderBy(gg => gg.Income).ToList(); + + // Assert + Check.That(result).IsNotNull(); + Check.That(result).HasSize(expected.Count); + Check.That(result.ToArray()[0]).Equals(expected[0]); + } + [Fact] public void ParseLambda_1() { @@ -306,4 +348,4 @@ public void ParseLambda_IllegalMethodCall_ThrowsException() .Throws().WithMessage("Methods on type 'Stream' are not accessible"); } } -} \ No newline at end of file +}