Skip to content

Commit a60226f

Browse files
authored
Fix DynamicExpressionParser for IQueryable (#802)
1 parent 284e93a commit a60226f

File tree

3 files changed

+38
-19
lines changed

3 files changed

+38
-19
lines changed

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

+6-10
Original file line numberDiff line numberDiff line change
@@ -1799,16 +1799,12 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string?
17991799

18001800
var isStaticAccess = expression == null;
18011801

1802-
if (!isStaticAccess)
1802+
if (!isStaticAccess && TypeHelper.TryFindGenericType(typeof(IEnumerable<>), type, out var enumerableType))
18031803
{
1804-
var enumerableType = TypeHelper.FindGenericType(typeof(IEnumerable<>), type);
1805-
if (enumerableType != null)
1804+
var elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0];
1805+
if (TryParseEnumerable(expression!, elementType, id, errorPos, type, out args, out var enumerableExpression))
18061806
{
1807-
var elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0];
1808-
if (TryParseEnumerable(expression!, elementType, id, errorPos, type, out args, out var enumerableExpression))
1809-
{
1810-
return enumerableExpression;
1811-
}
1807+
return enumerableExpression;
18121808
}
18131809
}
18141810

@@ -2075,7 +2071,7 @@ private bool TryParseEnumerable(Expression instance, Type elementType, string me
20752071
expression = null;
20762072
return false;
20772073
}
2078-
2074+
20792075
if (type != null && TypeHelper.IsDictionary(type) && _methodFinder.ContainsMethod(type, methodName, false))
20802076
{
20812077
var dictionaryMethod = type.GetMethod(methodName)!;
@@ -2087,7 +2083,7 @@ private bool TryParseEnumerable(Expression instance, Type elementType, string me
20872083
_methodFinder.CheckAggregateMethodAndTryUpdateArgsToMatchMethodArgs(methodName, ref args);
20882084

20892085
var callType = typeof(Enumerable);
2090-
if (type != null && TypeHelper.FindGenericType(typeof(IQueryable<>), type) != null && _methodFinder.ContainsMethod(type, methodName))
2086+
if (TypeHelper.TryFindGenericType(typeof(IQueryable<>), type, out _) && _methodFinder.ContainsMethod(typeof(Queryable), methodName))
20912087
{
20922088
callType = typeof(Queryable);
20932089
}

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

+10-9
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,32 @@ public static bool TryGetFirstGenericArgument(Type type, [NotNullWhen(true)] out
2020
return true;
2121
}
2222

23-
public static Type? FindGenericType(Type generic, Type? type)
23+
public static bool TryFindGenericType(Type generic, Type? type, [NotNullWhen(true)] out Type? foundType)
2424
{
2525
while (type != null && type != typeof(object))
2626
{
2727
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == generic)
2828
{
29-
return type;
29+
foundType = type;
30+
return true;
3031
}
3132

3233
if (generic.GetTypeInfo().IsInterface)
3334
{
34-
foreach (Type interfaceType in type.GetInterfaces())
35+
foreach (var interfaceType in type.GetInterfaces())
3536
{
36-
var foundType = FindGenericType(generic, interfaceType);
37-
if (foundType != null)
37+
if (TryFindGenericType(generic, interfaceType, out foundType))
3838
{
39-
return foundType;
39+
return true;
4040
}
4141
}
4242
}
4343

4444
type = type.GetTypeInfo().BaseType;
4545
}
4646

47-
return null;
47+
foundType = null;
48+
return false;
4849
}
4950

5051
public static bool IsCompatibleWith(Type source, Type target)
@@ -509,12 +510,12 @@ public static bool TryParseEnum(string value, Type? type, [NotNullWhen(true)] ou
509510
public static bool IsDictionary(Type? type)
510511
{
511512
return
512-
FindGenericType(typeof(IDictionary<,>), type) != null ||
513+
TryFindGenericType(typeof(IDictionary<,>), type, out _) ||
513514
#if NET35 || NET40
514515
// ReSharper disable once RedundantLogicalConditionalExpressionOperand
515516
false;
516517
#else
517-
FindGenericType(typeof(IReadOnlyDictionary<,>), type) != null;
518+
TryFindGenericType(typeof(IReadOnlyDictionary<,>), type, out _);
518519
#endif
519520
}
520521
}

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

+22
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Globalization;
33
using System.Linq.Dynamic.Core.CustomTypeProviders;
44
using System.Linq.Dynamic.Core.Exceptions;
5+
using System.Linq.Dynamic.Core.Tests.Helpers;
56
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
67
using System.Linq.Dynamic.Core.Tests.TestHelpers;
78
using System.Linq.Expressions;
@@ -577,6 +578,27 @@ public void DynamicExpressionParser_ParseLambda_Select_2()
577578
Assert.NotNull(result);
578579
}
579580

581+
// #801
582+
[Fact]
583+
public void DynamicExpressionParser_ParseLambda_IQueryable()
584+
{
585+
// Assign
586+
var qry = new[]
587+
{
588+
new
589+
{
590+
MessageClassName = "mc"
591+
}
592+
}.AsQueryable();
593+
594+
// Act
595+
var expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, "it.GroupBy(MessageClassName).Select(it.Key).Where(it != null).Distinct().OrderBy(it).Take(1000)");
596+
597+
// Assert
598+
expression.ToDebugView().Should().Contain(".Call System.Linq.Queryable");
599+
expression.ToDebugView().Should().NotContain(".Call System.Linq.Enumerable");
600+
}
601+
580602
// https://github.com/StefH/System.Linq.Dynamic.Core/issues/58
581603
[Fact]
582604
public void DynamicExpressionParser_ParseLambda_Issue58()

0 commit comments

Comments
 (0)