Skip to content

Commit 4cc72c4

Browse files
authored
Fix Average to support nullable types (#704)
1 parent d8754c2 commit 4cc72c4

File tree

3 files changed

+72
-35
lines changed

3 files changed

+72
-35
lines changed

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

+6
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,12 @@ public static string GetTypeName(Type? type)
417417
return name;
418418
}
419419

420+
public static Type GetNullableType(Type type)
421+
{
422+
type = Nullable.GetUnderlyingType(type) ?? type;
423+
return type.GetTypeInfo().IsValueType ? typeof(Nullable<>).MakeGenericType(type) : type;
424+
}
425+
420426
public static Type GetNonNullableType(Type type)
421427
{
422428
Check.NotNull(type, nameof(type));

src/System.Linq.Dynamic.Core/Util/QueryableMethodFinder.cs

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Linq.Expressions;
1+
using System.Collections.Generic;
2+
using System.Linq.Dynamic.Core.Parser;
3+
using System.Linq.Expressions;
24
using System.Reflection;
35

46
namespace System.Linq.Dynamic.Core.Util;
@@ -13,8 +15,16 @@ public static MethodInfo GetGenericMethod(string name)
1315
public static MethodInfo GetMethod(string name, Type argumentType, Type returnType, int parameterCount = 0, Func<MethodInfo, bool>? predicate = null) =>
1416
GetMethod(name, returnType, parameterCount, mi => mi.ToString().Contains(argumentType.ToString()) && ((predicate == null) || predicate(mi)));
1517

16-
public static MethodInfo GetMethod(string name, Type returnType, int parameterCount = 0, Func<MethodInfo, bool>? predicate = null) =>
17-
GetMethod(name, parameterCount, mi => (mi.ReturnType == returnType) && ((predicate == null) || predicate(mi)));
18+
public static MethodInfo GetMethod(string name, Type returnType, int parameterCount = 0, Func<MethodInfo, bool>? predicate = null)
19+
{
20+
var returnTypes = new List<Type> { returnType };
21+
if (!TypeHelper.IsNullableType(returnType))
22+
{
23+
returnTypes.Add(TypeHelper.GetNullableType(returnType));
24+
}
25+
26+
return GetMethod(name, parameterCount, mi => returnTypes.Contains(mi.ReturnType) && (predicate == null || predicate(mi)));
27+
}
1828

1929
public static MethodInfo GetMethodWithExpressionParameter(string name) =>
2030
GetMethod(name, 1, mi =>
@@ -35,7 +45,7 @@ public static MethodInfo GetMethod(string name, int parameterCount = 0, Func<Met
3545
{
3646
try
3747
{
38-
return typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).Single(mi =>
48+
return typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).First(mi =>
3949
mi.GetParameters().Length == parameterCount + 1 && (predicate == null || predicate(mi)));
4050
}
4151
catch (Exception ex)
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,57 @@
1-
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
1+
using System.Collections.Generic;
2+
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
23
using Xunit;
34

4-
namespace System.Linq.Dynamic.Core.Tests
5+
namespace System.Linq.Dynamic.Core.Tests;
6+
7+
public partial class QueryableTests
58
{
6-
public partial class QueryableTests
9+
[Fact]
10+
public void Average()
11+
{
12+
// Arrange
13+
var incomes = User.GenerateSampleModels(100).Select(u => u.Income).ToArray();
14+
15+
// Act
16+
var expected = incomes.Average();
17+
var actual = incomes.AsQueryable().Average();
18+
19+
// Assert
20+
Assert.Equal(expected, actual);
21+
}
22+
23+
[Fact]
24+
public void Average_Nullable()
25+
{
26+
// Arrange
27+
var list = new List<Entity> { new Entity { Value = 1 }, new Entity { Value = 2 }, new Entity { Value = null } };
28+
var queryable = list.AsQueryable();
29+
30+
31+
// Act
32+
var expected = queryable.Average(p => p.Value);
33+
var actual = queryable.Average("Value");
34+
35+
// Assert
36+
Assert.Equal(expected, actual);
37+
}
38+
39+
[Fact]
40+
public void Average_Selector()
41+
{
42+
// Arrange
43+
var users = User.GenerateSampleModels(100);
44+
45+
// Act
46+
var expected = users.Average(u => u.Income);
47+
var result = users.AsQueryable().Average("Income");
48+
49+
// Assert
50+
Assert.Equal(expected, result);
51+
}
52+
53+
public class Entity
754
{
8-
[Fact]
9-
public void Average()
10-
{
11-
// Arrange
12-
var incomes = User.GenerateSampleModels(100).Select(u => u.Income);
13-
14-
// Act
15-
var expected = incomes.Average();
16-
var actual = incomes.AsQueryable().Average();
17-
18-
// Assert
19-
Assert.Equal(expected, actual);
20-
}
21-
22-
[Fact]
23-
public void Average_Selector()
24-
{
25-
// Arrange
26-
var users = User.GenerateSampleModels(100);
27-
28-
// Act
29-
var expected = users.Average(u => u.Income);
30-
var result = users.AsQueryable().Average("Income");
31-
32-
// Assert
33-
Assert.Equal(expected, result);
34-
}
55+
public int? Value { get; set; }
3556
}
36-
}
57+
}

0 commit comments

Comments
 (0)