Skip to content

Commit

Permalink
Fix calling Sum without any arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
StefH committed Dec 8, 2024
1 parent 13ecca6 commit fee44aa
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 9 deletions.
9 changes: 5 additions & 4 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2113,18 +2113,19 @@ private bool TryParseEnumerable(Expression instance, Type enumerableType, string
}

// #794 - Check if the method is an aggregate (Average or Sum) method and try to update the arguments to match the method arguments
_methodFinder.CheckAggregateMethodAndTryUpdateArgsToMatchMethodArgs(methodName, ref args);
var isAggregateMethod = _methodFinder.CheckAggregateMethodAndTryUpdateArgsToMatchMethodArgs(methodName, ref args);

var callType = typeof(Enumerable);
if (TypeHelper.TryFindGenericType(typeof(IQueryable<>), type, out _) && _methodFinder.ContainsMethod(typeof(Queryable), methodName))
{
callType = typeof(Queryable);
}

// #633 - For Average without any arguments, try to find the non-generic Average method on the callType for the supplied parameter type.
if (methodName == nameof(Enumerable.Average) && args.Length == 0 && _methodFinder.TryFindAverageMethod(callType, theType, out var averageMethod))
// #633 / #856
// For Average/Sum without any arguments, try to find the non-generic Average/Sum method on the callType for the supplied parameter type.
if (isAggregateMethod && args.Length == 0 && _methodFinder.TryFindAggregateMethod(callType, methodName, theType, out var aggregateMethod))
{
expression = Expression.Call(null, averageMethod, instance);
expression = Expression.Call(null, aggregateMethod, instance);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,28 @@ public MethodFinder(ParsingConfig parsingConfig, IExpressionHelper expressionHel
_expressionHelper = Check.NotNull(expressionHelper);
}

public bool TryFindAverageMethod(Type callType, Type parameterType, [NotNullWhen(true)] out MethodInfo? averageMethod)
public bool TryFindAggregateMethod(Type callType, string methodName, Type parameterType, [NotNullWhen(true)] out MethodInfo? aggregateMethod)
{
averageMethod = callType
aggregateMethod = callType
.GetMethods()
.Where(m => m is { Name: nameof(Enumerable.Average), IsGenericMethodDefinition: false })
.Where(m => m.Name == methodName && !m.IsGenericMethodDefinition)
.SelectMany(m => m.GetParameters(), (m, p) => new { Method = m, Parameter = p })
.Where(x => x.Parameter.ParameterType == parameterType)
.Select(x => x.Method)
.FirstOrDefault();

return averageMethod != null;
return aggregateMethod != null;
}

public void CheckAggregateMethodAndTryUpdateArgsToMatchMethodArgs(string methodName, ref Expression[] args)
public bool CheckAggregateMethodAndTryUpdateArgsToMatchMethodArgs(string methodName, ref Expression[] args)
{
if (methodName is nameof(IAggregateSignatures.Average) or nameof(IAggregateSignatures.Sum))
{
ContainsMethod(typeof(IAggregateSignatures), methodName, false, null, ref args);
return true;
}

return false;
}

public bool ContainsMethod(Type type, string methodName, bool staticAccess = true)
Expand Down
64 changes: 64 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/QueryableTests.GroupBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,68 @@ public void GroupBy_Dynamic_SelectWhereAverageWithSelector()
// Assert
resultDynamic.Should().BeEquivalentTo(result);
}

[Fact]
public void GroupBy_Dynamic_SelectWhereSum()
{
// Arrange
var q = new[]
{
new DataSetA
{
I = 5
},
new DataSetA
{
I = 7
}
}
.AsQueryable();

// Act
var result = q
.GroupBy(x => x.Time)
.Select(x => new { q = x.Select(d => d.I).Where(d => d != null).Sum() })
.ToArray();

var resultDynamic = q
.GroupBy("Time")
.Select("new (Select(I).Where(it != null).Sum() as q)")
.ToDynamicArray();

// Assert
resultDynamic.Should().BeEquivalentTo(result);
}

[Fact]
public void GroupBy_Dynamic_SelectWhereSumWithSelector()
{
// Arrange
var q = new[]
{
new DataSetA
{
I = 5
},
new DataSetA
{
I = 7
}
}
.AsQueryable();

// Act
var result = q
.GroupBy(x => x.Time)
.Select(x => new { q = x.Select(d => d).Sum(y => y.I) })
.ToArray();

var resultDynamic = q
.GroupBy("Time")
.Select("new (Select(it).Sum(I) as q)")
.ToDynamicArray();

// Assert
resultDynamic.Should().BeEquivalentTo(result);
}
}

0 comments on commit fee44aa

Please sign in to comment.