Skip to content

Commit a4e4d93

Browse files
committed
support for generic (static) methods, thus also support for extension methods
(related to zzzprojects#386 )
1 parent dbd6a7f commit a4e4d93

File tree

6 files changed

+139
-8
lines changed

6 files changed

+139
-8
lines changed

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

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

1707-
if (expression == null)
1707+
if (method.IsGenericMethod)
17081708
{
1709-
return Expression.Call(null, method, args);
1709+
var genericParameters = method.GetParameters().Where(p => p.ParameterType.IsGenericParameter);
1710+
var typeArguments = genericParameters.Select(a => args[a.Position].Type);
1711+
var constructedMethod = method.MakeGenericMethod(typeArguments.ToArray());
1712+
return Expression.Call(expression, constructedMethod, args);
17101713
}
17111714
else
17121715
{

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

+55-2
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.Linq.Dynamic.Core.CustomTypeProviders;
33
using System.Linq.Dynamic.Core.Exceptions;
44
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
@@ -1070,7 +1070,7 @@ public void DynamicExpressionParser_ParseLambda_Operator_Less_Greater_With_Guids
10701070

10711071
// Assert
10721072
Assert.Equal(anotherId, result);
1073-
}
1073+
}
10741074

10751075
[Theory]
10761076
[InlineData("c => c.Age == 8", "c => (c.Age == 8)")]
@@ -1258,5 +1258,58 @@ public void DynamicExpressionParser_ParseLambda_String_TrimEnd_1_Parameter()
12581258
// Assert
12591259
result.Should().BeTrue();
12601260
}
1261+
1262+
public class DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider
1263+
{
1264+
public override HashSet<Type> GetCustomTypes() => new HashSet<Type>(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) };
1265+
}
1266+
1267+
[Fact]
1268+
public void DynamicExpressionParser_ParseLambda_GenericExtensionMethod()
1269+
{
1270+
// Arrange
1271+
var testList = User.GenerateSampleModels(51);
1272+
var config = new ParsingConfig()
1273+
{
1274+
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod()
1275+
};
1276+
1277+
// Act
1278+
string query = "x => MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User4\" || MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User2\"";
1279+
var expression = DynamicExpressionParser.ParseLambda<User, bool>(config, false, query);
1280+
var del = expression.Compile();
1281+
1282+
var result = Enumerable.Where(testList, del);
1283+
1284+
1285+
var expected = testList.Where(x => new string[] { "User4", "User2" }.Contains(x.UserName)).ToList();
1286+
1287+
// Assert
1288+
Check.That(result).IsNotNull();
1289+
Check.That(result).HasSize(expected.Count);
1290+
Check.That(result).Equals(expected);
1291+
}
1292+
1293+
//[Fact]
1294+
//public void DynamicExpressionParser_ParseLambda__GenericExtension()
1295+
//{
1296+
// var config = new ParsingConfig
1297+
// {
1298+
// CustomTypeProvider = new ExpressionTests.DefaultDynamicLinqCustomTypeProviderForStaticTesting()
1299+
// };
1300+
1301+
// // Arrange
1302+
// var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray();
1303+
1304+
// // Act
1305+
// var methods = new Methods();
1306+
// var expectedResult = list.Where(x => MethodsItemExtension.Functions.EfCoreCollate(x.Value, "tlh-KX") == 2);
1307+
// var result = list.AsQueryable().Where(config, "MethodsItemExtension.Functions.EfCoreCollate(it.Value,\"tlh-KX\")==2");
1308+
1309+
// // Assert
1310+
// Assert.Equal(expectedResult.Count(), result.Count());
1311+
//}
1312+
1313+
12611314
}
12621315
}

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)