Skip to content

Commit d2850c8

Browse files
david-garcia-garciaStefH
authored andcommitted
Fix for #294 (#297)
* only test * fix * xx * xx
1 parent de0750d commit d2850c8

File tree

3 files changed

+56
-5
lines changed

3 files changed

+56
-5
lines changed

src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static LambdaExpression ParseLambda([CanBeNull] ParsingConfig parsingConf
6868

6969
if (parsingConfig != null && parsingConfig.RenameParameterExpression && parameters.Length == 1)
7070
{
71-
var renamer = new ParameterExpressionRenamer(parser.ItName);
71+
var renamer = new ParameterExpressionRenamer(parser.LastLambdaItName);
7272
parsedExpression = renamer.Rename(parsedExpression, out ParameterExpression newParameterExpression);
7373

7474
return Expression.Lambda(parsedExpression, new[] { newParameterExpression });

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

+18-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ public class ExpressionParser
4646
/// </summary>
4747
public string ItName { get; private set; } = KeywordsHelper.KEYWORD_IT;
4848

49+
/// <summary>
50+
/// There was a problem when an expression contained multiple lambdas where
51+
/// the ItName was not cleared and freed for the next lambda. This variable
52+
/// store the ItName of the last parsed lambda. Not used internally by
53+
/// ExpressionParser, but used to preserve compatiblity of parsingConfig.RenameParameterExpression
54+
/// which was designed to wonly work with mono-lambda expressions
55+
/// </summary>
56+
public string LastLambdaItName { get; private set; } = KeywordsHelper.KEYWORD_IT;
57+
4958
/// <summary>
5059
/// Initializes a new instance of the <see cref="ExpressionParser"/> class.
5160
/// </summary>
@@ -1669,6 +1678,7 @@ Expression ParseMemberAccess(Type type, Expression instance)
16691678
{
16701679
// This might be an internal variable for use within a lambda expression, so store it as such
16711680
_internals.Add(id, _it);
1681+
string _previousItName = ItName;
16721682

16731683
// Also store ItName (only once)
16741684
if (string.Equals(ItName, KeywordsHelper.KEYWORD_IT))
@@ -1679,7 +1689,14 @@ Expression ParseMemberAccess(Type type, Expression instance)
16791689
// next
16801690
_textParser.NextToken();
16811691

1682-
return ParseConditionalOperator();
1692+
LastLambdaItName = ItName;
1693+
var exp = ParseConditionalOperator();
1694+
1695+
// Restore previous context and clear internals
1696+
_internals.Remove(id);
1697+
ItName = _previousItName;
1698+
1699+
return exp;
16831700
}
16841701

16851702
throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type));

test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs

+37-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
using System.Collections.Generic;
1+
using NFluent;
2+
using System.Collections.Generic;
23
using System.Linq.Dynamic.Core.CustomTypeProviders;
34
using System.Linq.Dynamic.Core.Exceptions;
45
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
56
using System.Linq.Expressions;
67
using System.Reflection;
7-
using NFluent;
88
using Xunit;
99
using User = System.Linq.Dynamic.Core.Tests.Helpers.Models.User;
1010

@@ -256,7 +256,7 @@ public void DynamicExpressionParser_ParseLambda_WithStructWithEquality(string qu
256256
var qry = testList.AsQueryable();
257257

258258
// Act
259-
ulong expectedX = (ulong) long.MaxValue + 3;
259+
ulong expectedX = (ulong)long.MaxValue + 3;
260260

261261
query = string.Format(query, expectedX);
262262
LambdaExpression expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query);
@@ -613,6 +613,40 @@ public void DynamicExpressionParser_ParseLambda_StringLiteralEmbeddedQuote_Retur
613613
Assert.Equal(expectedRightValue, rightValue);
614614
}
615615

616+
/// <summary>
617+
/// @see https://github.com/StefH/System.Linq.Dynamic.Core/issues/294
618+
/// </summary>
619+
[Fact]
620+
public void DynamicExpressionParser_ParseLambda_MultipleLambdas()
621+
{
622+
var users = new[]
623+
{
624+
new { name = "Juan", age = 25 },
625+
new { name = "Juan", age = 25 },
626+
new { name = "David", age = 12 },
627+
new { name = "Juan", age = 25 },
628+
new { name = "Juan", age = 4 },
629+
new { name = "Pedro", age = 2 },
630+
new { name = "Juan", age = 25 }
631+
}.ToList();
632+
633+
IQueryable query;
634+
635+
// One lambda
636+
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}]}]";
637+
query = users.AsQueryable();
638+
query = query.GroupBy("new(name as name)", "it");
639+
query = query.Select("new (it.Key as Key, new(it.Sum(x => x.age) as ageSum) as nativeAggregates, it as Grouping)");
640+
Assert.Equal(res1, Newtonsoft.Json.JsonConvert.SerializeObject(query));
641+
642+
// Multiple lambdas
643+
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}]}]";
644+
query = users.AsQueryable();
645+
query = query.GroupBy("new(name as name)", "it");
646+
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)");
647+
Assert.Equal(res2, Newtonsoft.Json.JsonConvert.SerializeObject(query));
648+
}
649+
616650
[Fact]
617651
public void DynamicExpressionParser_ParseLambda_StringLiteralStartEmbeddedQuote_ReturnsBooleanLambdaExpression()
618652
{

0 commit comments

Comments
 (0)