Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down
19 changes: 18 additions & 1 deletion src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ public class ExpressionParser
/// </summary>
public string ItName { get; private set; } = KeywordsHelper.KEYWORD_IT;

/// <summary>
/// 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
/// </summary>
public string LastLambdaItName { get; private set; } = KeywordsHelper.KEYWORD_IT;

/// <summary>
/// Initializes a new instance of the <see cref="ExpressionParser"/> class.
/// </summary>
Expand Down Expand Up @@ -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))
Expand All @@ -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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -613,6 +613,40 @@ public void DynamicExpressionParser_ParseLambda_StringLiteralEmbeddedQuote_Retur
Assert.Equal(expectedRightValue, rightValue);
}

/// <summary>
/// @see https://github.com/StefH/System.Linq.Dynamic.Core/issues/294
/// </summary>
[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()
{
Expand Down