This issue is potentialy related or equal to:
#288
#259
I prepared a simple test case:
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;
string results;
// This does work
query = users.AsQueryable();
query = query.GroupBy(CustomParsingConfig.ParsingConfig, "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)");
results = JsonConvert.SerializeObject(query);
// This miserable fails :)
query = users.AsQueryable();
query = query.GroupBy(CustomParsingConfig.ParsingConfig, "new(name as name)", "it");
query = query.Select("new (it.Key as Key, new(Enumerable.Sum(it, x => x.age) as ageSum) as nativeAggregates, it as Grouping)");
results = JsonConvert.SerializeObject(query);
I've been looking through the code and the fix looks quite complex.
What happens now is that the ParseArguments() method in ExpressionParser does not properly handle generic types or I even presume specific types for lambda expressions.
Expression[] ParseArguments()
{
var argList = new List<Expression>();
while (true)
{
var argumentExpression = ParseConditionalOperator();
_expressionHelper.WrapConstantExpression(ref argumentExpression);
argList.Add(argumentExpression);
if (_textParser.CurrentToken.Id != TokenId.Comma)
{
break;
}
_textParser.NextToken();
}
return argList.ToArray();
}
This code is basically parsing the arguments one by one, without any previous knowledge of any potential relationship between them, or where are they consumed.
For example, take this method signature:
public string DoRandomStuff<T>(Func<T, string> projection, T arg) {
return projection(arg) + ";";
}
If we want to call this using DynamicLinq:
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().AsQueryable();
var test = users.Select("DoRandomStuff(~, x => x.age)");
If this previous code works, it's just a coincidence, because when parsing the arguments for DoRandomStuff the ParseArguments() and related logic is not saying, ok so parameter 1 for the method is of type T, then X must be of type T.
Solving the issue is difficult because you don't have precise knowledge of what overload (if the method has many overloads) you need to use to do this type inference. Indeed, this cannot be exactly known until you parse all arguments which is fish that bite's its tail problem.
A different approach would be to parse all lambdas assuming lambda arguments are dynamic types, and once all method arguments have been parsed, have some logic resolve what is the most suitable overload of the method to use, and then replace (or rebuild the expression) with the properly inferred types.
This issue is potentialy related or equal to:
#288
#259
I prepared a simple test case:
I've been looking through the code and the fix looks quite complex.
What happens now is that the ParseArguments() method in ExpressionParser does not properly handle generic types or I even presume specific types for lambda expressions.
This code is basically parsing the arguments one by one, without any previous knowledge of any potential relationship between them, or where are they consumed.
For example, take this method signature:
If we want to call this using DynamicLinq:
If this previous code works, it's just a coincidence, because when parsing the arguments for DoRandomStuff the ParseArguments() and related logic is not saying, ok so parameter 1 for the method is of type T, then X must be of type T.
Solving the issue is difficult because you don't have precise knowledge of what overload (if the method has many overloads) you need to use to do this type inference. Indeed, this cannot be exactly known until you parse all arguments which is fish that bite's its tail problem.
A different approach would be to parse all lambdas assuming lambda arguments are dynamic types, and once all method arguments have been parsed, have some logic resolve what is the most suitable overload of the method to use, and then replace (or rebuild the expression) with the properly inferred types.