Skip to content

Commit d548238

Browse files
authored
Fix calling Sum without any arguments (#861)
1 parent 13ecca6 commit d548238

File tree

3 files changed

+77
-9
lines changed

3 files changed

+77
-9
lines changed

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -2113,18 +2113,19 @@ private bool TryParseEnumerable(Expression instance, Type enumerableType, string
21132113
}
21142114

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

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

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

src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs

+8-5
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,28 @@ public MethodFinder(ParsingConfig parsingConfig, IExpressionHelper expressionHel
4545
_expressionHelper = Check.NotNull(expressionHelper);
4646
}
4747

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

58-
return averageMethod != null;
58+
return aggregateMethod != null;
5959
}
6060

61-
public void CheckAggregateMethodAndTryUpdateArgsToMatchMethodArgs(string methodName, ref Expression[] args)
61+
public bool CheckAggregateMethodAndTryUpdateArgsToMatchMethodArgs(string methodName, ref Expression[] args)
6262
{
6363
if (methodName is nameof(IAggregateSignatures.Average) or nameof(IAggregateSignatures.Sum))
6464
{
6565
ContainsMethod(typeof(IAggregateSignatures), methodName, false, null, ref args);
66+
return true;
6667
}
68+
69+
return false;
6770
}
6871

6972
public bool ContainsMethod(Type type, string methodName, bool staticAccess = true)

test/System.Linq.Dynamic.Core.Tests/QueryableTests.GroupBy.cs

+64
Original file line numberDiff line numberDiff line change
@@ -275,4 +275,68 @@ public void GroupBy_Dynamic_SelectWhereAverageWithSelector()
275275
// Assert
276276
resultDynamic.Should().BeEquivalentTo(result);
277277
}
278+
279+
[Fact]
280+
public void GroupBy_Dynamic_SelectWhereSum()
281+
{
282+
// Arrange
283+
var q = new[]
284+
{
285+
new DataSetA
286+
{
287+
I = 5
288+
},
289+
new DataSetA
290+
{
291+
I = 7
292+
}
293+
}
294+
.AsQueryable();
295+
296+
// Act
297+
var result = q
298+
.GroupBy(x => x.Time)
299+
.Select(x => new { q = x.Select(d => d.I).Where(d => d != null).Sum() })
300+
.ToArray();
301+
302+
var resultDynamic = q
303+
.GroupBy("Time")
304+
.Select("new (Select(I).Where(it != null).Sum() as q)")
305+
.ToDynamicArray();
306+
307+
// Assert
308+
resultDynamic.Should().BeEquivalentTo(result);
309+
}
310+
311+
[Fact]
312+
public void GroupBy_Dynamic_SelectWhereSumWithSelector()
313+
{
314+
// Arrange
315+
var q = new[]
316+
{
317+
new DataSetA
318+
{
319+
I = 5
320+
},
321+
new DataSetA
322+
{
323+
I = 7
324+
}
325+
}
326+
.AsQueryable();
327+
328+
// Act
329+
var result = q
330+
.GroupBy(x => x.Time)
331+
.Select(x => new { q = x.Select(d => d).Sum(y => y.I) })
332+
.ToArray();
333+
334+
var resultDynamic = q
335+
.GroupBy("Time")
336+
.Select("new (Select(it).Sum(I) as q)")
337+
.ToDynamicArray();
338+
339+
// Assert
340+
resultDynamic.Should().BeEquivalentTo(result);
341+
}
278342
}

0 commit comments

Comments
 (0)