Skip to content

Commit cc7a0a6

Browse files
authored
support for generic (static) methods, thus also support for extension… (#517)
* support for generic (static) methods, thus also support for extension methods (related to #386 ) * support for generic (static) methods, thus also support for extension methods (related to #386 )
1 parent 65bf6f6 commit cc7a0a6

File tree

6 files changed

+116
-7
lines changed

6 files changed

+116
-7
lines changed

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -1695,9 +1695,12 @@ Expression ParseMemberAccess(Type type, Expression expression)
16951695
throw ParseError(errorPos, Res.MethodsAreInaccessible, TypeHelper.GetTypeName(method.DeclaringType));
16961696
}
16971697

1698-
if (expression == null)
1698+
if (method.IsGenericMethod)
16991699
{
1700-
return Expression.Call(null, method, args);
1700+
var genericParameters = method.GetParameters().Where(p => p.ParameterType.IsGenericParameter);
1701+
var typeArguments = genericParameters.Select(a => args[a.Position].Type);
1702+
var constructedMethod = method.MakeGenericMethod(typeArguments.ToArray());
1703+
return Expression.Call(expression, constructedMethod, args);
17011704
}
17021705
else
17031706
{

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public ExpressionPromoter(ParsingConfig config)
2222
/// <inheritdoc cref="IExpressionPromoter.Promote(Expression, Type, bool, bool)"/>
2323
public virtual Expression Promote(Expression expr, Type type, bool exact, bool convertExpr)
2424
{
25-
if (expr.Type == type)
25+
if (expr.Type == type || type.IsGenericParameter)
2626
{
2727
return expr;
2828
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public int FindMethod(Type type, string methodName, bool staticAccess, ref Expre
5858
{
5959
if (_parsingConfig.CustomTypeProvider.GetExtensionMethods().TryGetValue(t, out var extensionMethodsOfType))
6060
{
61-
methods.AddRange(extensionMethodsOfType.Where(m => m.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase) && !m.IsGenericMethod));
61+
methods.AddRange(extensionMethodsOfType.Where(m => m.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase)));
6262
}
6363
}
6464

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

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Globalization;
33
using System.Linq.Dynamic.Core.CustomTypeProviders;
44
using System.Linq.Dynamic.Core.Exceptions;
@@ -1354,5 +1354,36 @@ public void DynamicExpressionParser_ParseLambda_String_TrimEnd_1_Parameter()
13541354
// Assert
13551355
result.Should().BeTrue();
13561356
}
1357+
1358+
public class DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider
1359+
{
1360+
public override HashSet<Type> GetCustomTypes() => new HashSet<Type>(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) };
1361+
}
1362+
1363+
[Fact]
1364+
public void DynamicExpressionParser_ParseLambda_GenericExtensionMethod()
1365+
{
1366+
// Arrange
1367+
var testList = User.GenerateSampleModels(51);
1368+
var config = new ParsingConfig()
1369+
{
1370+
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod()
1371+
};
1372+
1373+
// Act
1374+
string query = "x => MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User4\" || MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User2\"";
1375+
var expression = DynamicExpressionParser.ParseLambda<User, bool>(config, false, query);
1376+
var del = expression.Compile();
1377+
1378+
var result = Enumerable.Where(testList, del);
1379+
1380+
1381+
var expected = testList.Where(x => new string[] { "User4", "User2" }.Contains(x.UserName)).ToList();
1382+
1383+
// Assert
1384+
Check.That(result).IsNotNull();
1385+
Check.That(result).HasSize(expected.Count);
1386+
Check.That(result).Equals(expected);
1387+
}
13571388
}
13581389
}

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

+64-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Dynamic;
33
using System.Globalization;
44
using System.Linq.Dynamic.Core.Exceptions;
@@ -1304,6 +1304,69 @@ public void ExpressionTests_Method_OneParam_With_it()
13041304
Assert.Equal(expected.Count(), result.Count());
13051305
}
13061306

1307+
public class DefaultDynamicLinqCustomTypeProviderForStaticTesting : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider
1308+
{
1309+
public override HashSet<Type> GetCustomTypes() => new HashSet<Type>(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) };
1310+
}
1311+
1312+
[Fact]
1313+
public void ExpressionTests_MethodCall_GenericStatic()
1314+
{
1315+
var config = new ParsingConfig
1316+
{
1317+
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting()
1318+
};
1319+
1320+
// Arrange
1321+
var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray();
1322+
1323+
// Act
1324+
var expectedResult = list.Where(x => Methods.StaticGenericMethod(x));
1325+
var result = list.AsQueryable().Where(config, "Methods.StaticGenericMethod(it)");
1326+
1327+
// Assert
1328+
Assert.Equal(expectedResult.Count(), result.Count());
1329+
}
1330+
1331+
[Fact]
1332+
public void ExpressionTests_MethodCall_Generic()
1333+
{
1334+
var config = new ParsingConfig
1335+
{
1336+
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting()
1337+
};
1338+
1339+
// Arrange
1340+
var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray();
1341+
1342+
// Act
1343+
var methods = new Methods();
1344+
var expectedResult = list.Where(x => methods.GenericMethod(x));
1345+
var result = list.AsQueryable().Where("@0.GenericMethod(it)", methods);
1346+
1347+
// Assert
1348+
Assert.Equal(expectedResult.Count(), result.Count());
1349+
}
1350+
1351+
[Fact]
1352+
public void ExpressionTests_MethodCall_GenericExtension()
1353+
{
1354+
var config = new ParsingConfig
1355+
{
1356+
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting()
1357+
};
1358+
1359+
// Arrange
1360+
var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray();
1361+
1362+
// Act
1363+
var methods = new Methods();
1364+
var expectedResult = list.Where(x => MethodsItemExtension.Functions.EfCoreCollate(x.Value, "tlh-KX") == 2);
1365+
var result = list.AsQueryable().Where(config, "MethodsItemExtension.Functions.EfCoreCollate(it.Value,\"tlh-KX\")==2");
1366+
1367+
// Assert
1368+
Assert.Equal(expectedResult.Count(), result.Count());
1369+
}
13071370

13081371
[Fact]
13091372
public void ExpressionTests_MethodCall_ValueTypeToValueTypeParameter()

test/System.Linq.Dynamic.Core.Tests/Helpers/Models/Methods.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace System.Linq.Dynamic.Core.Tests.Helpers.Models
1+
namespace System.Linq.Dynamic.Core.Tests.Helpers.Models
22
{
33
public class Methods
44
{
@@ -22,5 +22,17 @@ public class Item
2222
{
2323
public int Value { get; set; }
2424
}
25+
26+
public static bool StaticGenericMethod<T>(T value) => value is Item item && item.Value == 1;
27+
public bool GenericMethod<T>(T value) => value is Item item && item.Value == 1;
28+
29+
}
30+
31+
public static class MethodsItemExtension
32+
{
33+
public class DummyFunctions { }
34+
public static DummyFunctions Functions => new DummyFunctions();
35+
36+
public static T EfCoreCollate<T>(this DummyFunctions _, T value, string collation) => value;
2537
}
2638
}

0 commit comments

Comments
 (0)