diff --git a/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs b/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs
index d644aaba..c48e7a3f 100644
--- a/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs
@@ -68,7 +68,7 @@ public static LambdaExpression ParseLambda([CanBeNull] ParsingConfig parsingConf
if (parsingConfig != null && parsingConfig.RenameParameterExpression && parameters.Length == 1)
{
- var renamer = new ParameterExpressionRenamer(parser.ItName);
+ var renamer = new ParameterExpressionRenamer(parser.LastLambdaItName);
parsedExpression = renamer.Rename(parsedExpression, out ParameterExpression newParameterExpression);
return Expression.Lambda(parsedExpression, new[] { newParameterExpression });
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index 07ef88c9..a9bfbd90 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -44,6 +44,15 @@ public class ExpressionParser
///
public string ItName { get; private set; } = KeywordsHelper.KEYWORD_IT;
+ ///
+ /// There was a problem when an expression contained multiple lambdas where
+ /// the ItName was not cleared and freed for the next lambda. This variable
+ /// store the ItName of the last parsed lambda. Not used internally by
+ /// ExpressionParser, but used to preserve compatiblity of parsingConfig.RenameParameterExpression
+ /// which was designed to wonly work with mono-lambda expressions
+ ///
+ public string LastLambdaItName { get; private set; } = KeywordsHelper.KEYWORD_IT;
+
///
/// Initializes a new instance of the class.
///
@@ -1676,6 +1685,7 @@ Expression ParseMemberAccess(Type type, Expression instance)
{
// This might be an internal variable for use within a lambda expression, so store it as such
_internals.Add(id, _it);
+ string _previousItName = ItName;
// Also store ItName (only once)
if (string.Equals(ItName, KeywordsHelper.KEYWORD_IT))
@@ -1686,7 +1696,14 @@ Expression ParseMemberAccess(Type type, Expression instance)
// next
_textParser.NextToken();
- return ParseConditionalOperator();
+ LastLambdaItName = ItName;
+ var exp = ParseConditionalOperator();
+
+ // Restore previous context and clear internals
+ _internals.Remove(id);
+ ItName = _previousItName;
+
+ return exp;
}
throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type));
diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
index 7cd30ac9..dee9d5c0 100644
--- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
@@ -1,10 +1,10 @@
-using System.Collections.Generic;
+using NFluent;
+using System.Collections.Generic;
using System.Linq.Dynamic.Core.CustomTypeProviders;
using System.Linq.Dynamic.Core.Exceptions;
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
using System.Linq.Expressions;
using System.Reflection;
-using NFluent;
using Xunit;
using User = System.Linq.Dynamic.Core.Tests.Helpers.Models.User;
@@ -256,7 +256,7 @@ public void DynamicExpressionParser_ParseLambda_WithStructWithEquality(string qu
var qry = testList.AsQueryable();
// Act
- ulong expectedX = (ulong) long.MaxValue + 3;
+ ulong expectedX = (ulong)long.MaxValue + 3;
query = string.Format(query, expectedX);
LambdaExpression expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query);
@@ -613,6 +613,40 @@ public void DynamicExpressionParser_ParseLambda_StringLiteralEmbeddedQuote_Retur
Assert.Equal(expectedRightValue, rightValue);
}
+ ///
+ /// @see https://github.com/StefH/System.Linq.Dynamic.Core/issues/294
+ ///
+ [Fact]
+ public void DynamicExpressionParser_ParseLambda_MultipleLambdas()
+ {
+ var users = new[]
+ {
+ new { name = "Juan", age = 25 },
+ new { name = "Juan", age = 25 },
+ new { name = "David", age = 12 },
+ new { name = "Juan", age = 25 },
+ new { name = "Juan", age = 4 },
+ new { name = "Pedro", age = 2 },
+ new { name = "Juan", age = 25 }
+ }.ToList();
+
+ IQueryable query;
+
+ // One lambda
+ string res1 = "[{\"Key\":{\"name\":\"Juan\"},\"nativeAggregates\":{\"ageSum\":104},\"Grouping\":[{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":4},{\"name\":\"Juan\",\"age\":25}]},{\"Key\":{\"name\":\"David\"},\"nativeAggregates\":{\"ageSum\":12},\"Grouping\":[{\"name\":\"David\",\"age\":12}]},{\"Key\":{\"name\":\"Pedro\"},\"nativeAggregates\":{\"ageSum\":2},\"Grouping\":[{\"name\":\"Pedro\",\"age\":2}]}]";
+ query = users.AsQueryable();
+ query = query.GroupBy("new(name as name)", "it");
+ query = query.Select("new (it.Key as Key, new(it.Sum(x => x.age) as ageSum) as nativeAggregates, it as Grouping)");
+ Assert.Equal(res1, Newtonsoft.Json.JsonConvert.SerializeObject(query));
+
+ // Multiple lambdas
+ string res2 = "[{\"Key\":{\"name\":\"Juan\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":104},\"Grouping\":[{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":4},{\"name\":\"Juan\",\"age\":25}]},{\"Key\":{\"name\":\"David\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":12},\"Grouping\":[{\"name\":\"David\",\"age\":12}]},{\"Key\":{\"name\":\"Pedro\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":2},\"Grouping\":[{\"name\":\"Pedro\",\"age\":2}]}]";
+ query = users.AsQueryable();
+ query = query.GroupBy("new(name as name)", "it");
+ query = query.Select("new (it.Key as Key, new(it.Sum(x => x.age > 25 ? 1 : 0) as ageSum, it.Sum(x => x.age) as ageSum2) as nativeAggregates, it as Grouping)");
+ Assert.Equal(res2, Newtonsoft.Json.JsonConvert.SerializeObject(query));
+ }
+
[Fact]
public void DynamicExpressionParser_ParseLambda_StringLiteralStartEmbeddedQuote_ReturnsBooleanLambdaExpression()
{