Skip to content

Commit 2c49cb0

Browse files
authored
Fix FindMethod for extension methods (#510)
1 parent 9ca9b34 commit 2c49cb0

File tree

3 files changed

+32
-18
lines changed

3 files changed

+32
-18
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1550,7 +1550,7 @@ Expression ParseTypeAccess(Type type)
15501550
var constructorsWithOutPointerArguments = type.GetConstructors()
15511551
.Where(c => !c.GetParameters().Any(p => p.ParameterType.GetTypeInfo().IsPointer))
15521552
.ToArray();
1553-
switch (_methodFinder.FindBestMethod(constructorsWithOutPointerArguments, ref args, out MethodBase method))
1553+
switch (_methodFinder.FindBestMethodBasedOnArguments(constructorsWithOutPointerArguments, ref args, out MethodBase method))
15541554
{
15551555
case 0:
15561556
if (args.Length == 1 && TryGenerateConversion(args[0], type, out generatedExpression))

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

+22-13
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public int FindMethod(Type type, string methodName, bool staticAccess, ref Expre
3232
foreach (Type t in SelfAndBaseTypes(type))
3333
{
3434
MemberInfo[] members = t.FindMembers(MemberTypes.Method, flags, Type.FilterNameIgnoreCase, methodName);
35-
int count = FindBestMethod(members.Cast<MethodBase>(), ref args, out method);
35+
int count = FindBestMethodBasedOnArguments(members.Cast<MethodBase>(), ref args, out method);
3636
if (count != 0)
3737
{
3838
return count;
@@ -41,8 +41,8 @@ public int FindMethod(Type type, string methodName, bool staticAccess, ref Expre
4141
#else
4242
foreach (Type t in SelfAndBaseTypes(type))
4343
{
44-
MethodInfo[] methods = t.GetTypeInfo().DeclaredMethods.Where(x => (x.IsStatic || !staticAccess) && x.Name.ToLowerInvariant() == methodName.ToLowerInvariant()).ToArray();
45-
int count = FindBestMethod(methods, ref args, out method);
44+
var methods = t.GetTypeInfo().DeclaredMethods.Where(m => (m.IsStatic || !staticAccess) && m.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase)).ToArray();
45+
int count = FindBestMethodBasedOnArguments(methods, ref args, out method);
4646
if (count != 0)
4747
{
4848
return count;
@@ -52,13 +52,23 @@ public int FindMethod(Type type, string methodName, bool staticAccess, ref Expre
5252

5353
if (instance != null)
5454
{
55-
// Try to solve with registered extension methods
56-
if (_parsingConfig.CustomTypeProvider.GetExtensionMethods().TryGetValue(type, out var methods))
55+
// Try to solve with registered extension methods from this type and all base types
56+
var methods = new List<MethodInfo>();
57+
foreach (var t in SelfAndBaseTypes(type))
58+
{
59+
if (_parsingConfig.CustomTypeProvider.GetExtensionMethods().TryGetValue(t, out var extensionMethodsOfType))
60+
{
61+
methods.AddRange(extensionMethodsOfType.Where(m => m.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase) && !m.IsGenericMethod));
62+
}
63+
}
64+
65+
if (methods.Any())
5766
{
5867
var argsList = args.ToList();
5968
argsList.Insert(0, instance);
69+
6070
var extensionMethodArgs = argsList.ToArray();
61-
int count = FindBestMethod(methods.Cast<MethodBase>(), ref extensionMethodArgs, out method);
71+
int count = FindBestMethodBasedOnArguments(methods.Cast<MethodBase>(), ref extensionMethodArgs, out method);
6272
if (count != 0)
6373
{
6474
instance = null;
@@ -72,10 +82,9 @@ public int FindMethod(Type type, string methodName, bool staticAccess, ref Expre
7282
return 0;
7383
}
7484

75-
public int FindBestMethod(IEnumerable<MethodBase> methods, ref Expression[] args, out MethodBase method)
85+
public int FindBestMethodBasedOnArguments(IEnumerable<MethodBase> methods, ref Expression[] args, out MethodBase method)
7686
{
77-
// passing args by reference is now required with the params array support.
78-
87+
// Passing args by reference is now required with the params array support.
7988
var inlineArgs = args;
8089

8190
MethodData[] applicable = methods
@@ -85,7 +94,7 @@ public int FindBestMethod(IEnumerable<MethodBase> methods, ref Expression[] args
8594

8695
if (applicable.Length > 1)
8796
{
88-
applicable = applicable.Where(m => applicable.All(n => m == n || IsBetterThan(inlineArgs, m, n))).ToArray();
97+
applicable = applicable.Where(m => applicable.All(n => m == n || FirstIsBetterThanSecond(inlineArgs, m, n))).ToArray();
8998
}
9099

91100
if (args.Length == 2 && applicable.Length > 1 && (args[0].Type == typeof(Guid?) || args[1].Type == typeof(Guid?)))
@@ -121,7 +130,7 @@ public int FindIndexer(Type type, Expression[] args, out MethodBase method)
121130
#else
122131
Select(p => (MethodBase)p.GetMethod);
123132
#endif
124-
int count = FindBestMethod(methods, ref args, out method);
133+
int count = FindBestMethodBasedOnArguments(methods, ref args, out method);
125134
if (count != 0)
126135
{
127136
return count;
@@ -203,9 +212,9 @@ bool IsApplicable(MethodData method, Expression[] args)
203212
return true;
204213
}
205214

206-
bool IsBetterThan(Expression[] args, MethodData first, MethodData second)
215+
bool FirstIsBetterThanSecond(Expression[] args, MethodData first, MethodData second)
207216
{
208-
// If args count is 0 -> parametereless method is better than method method with parameters
217+
// If args count is 0 -> parameterless method is better than method method with parameters
209218
if (args.Length == 0)
210219
{
211220
return first.Parameters.Length == 0 && second.Parameters.Length != 0;

test/System.Linq.Dynamic.Core.Tests/Parser/DynamicLinqTypeTest.cs

+9-4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ public static int IncrementMe(this int values, int y)
3636
{
3737
return values + y;
3838
}
39+
40+
public static int IncrementMeAlso(this int values)
41+
{
42+
return values + 1;
43+
}
3944
}
4045

4146
public class DynamicLinqTypeTest
@@ -64,7 +69,7 @@ public void ParamArray_NullValue()
6469
var query = "Utils.ConvertToArray(null)";
6570
var expression = DynamicExpressionParser.ParseLambda(null, query, null);
6671
var del = expression.Compile();
67-
var result = (string[]) del.DynamicInvoke();
72+
var result = (string[])del.DynamicInvoke();
6873

6974
Check.That(result).IsNull();
7075
}
@@ -75,7 +80,7 @@ public void ParamArray_WithManyValue()
7580
var query = "Utils.ConvertToArray(\"a\", \"b\")";
7681
var expression = DynamicExpressionParser.ParseLambda(null, query, null);
7782
var del = expression.Compile();
78-
var result = (string[]) del.DynamicInvoke();
83+
var result = (string[])del.DynamicInvoke();
7984

8085
Check.That(result.Length).Equals(2);
8186
Check.That(result[0]).Equals("a");
@@ -88,7 +93,7 @@ public void ParamArray_WithSingleValue()
8893
var query = "Utils.ConvertToArray(\"a\")";
8994
var expression = DynamicExpressionParser.ParseLambda(null, query, null);
9095
var del = expression.Compile();
91-
var result = (string[]) del.DynamicInvoke();
96+
var result = (string[])del.DynamicInvoke();
9297

9398
Check.That(result.Length).Equals(1);
9499
Check.That(result[0]).Equals("a");
@@ -145,7 +150,7 @@ public void ParamArray_Array()
145150
[Fact]
146151
public void ExtensionMethod_NoParameter()
147152
{
148-
var list = new[] {new EntityValue {ValueInt = 1}, new EntityValue {ValueInt = 2}}.AsQueryable();
153+
var list = new[] { new EntityValue { ValueInt = 1 }, new EntityValue { ValueInt = 2 } }.AsQueryable();
149154
var result = list.Select("ValueInt.IncrementMe()").ToDynamicList<int>();
150155

151156
Check.That(result.Count).Equals(2);

0 commit comments

Comments
 (0)