diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index c871568e..1edb773e 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -1704,9 +1704,12 @@ Expression ParseMemberAccess(Type type, Expression expression)
throw ParseError(errorPos, Res.MethodsAreInaccessible, TypeHelper.GetTypeName(method.DeclaringType));
}
- if (expression == null)
+ if (method.IsGenericMethod)
{
- return Expression.Call(null, method, args);
+ var genericParameters = method.GetParameters().Where(p => p.ParameterType.IsGenericParameter);
+ var typeArguments = genericParameters.Select(a => args[a.Position].Type);
+ var constructedMethod = method.MakeGenericMethod(typeArguments.ToArray());
+ return Expression.Call(expression, constructedMethod, args);
}
else
{
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
index 71770b95..7ee3ec86 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
@@ -22,7 +22,7 @@ public ExpressionPromoter(ParsingConfig config)
///
public virtual Expression Promote(Expression expr, Type type, bool exact, bool convertExpr)
{
- if (expr.Type == type)
+ if (expr.Type == type || type.IsGenericParameter)
{
return expr;
}
diff --git a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs
index 0cf6f7e4..1e2a8efe 100644
--- a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs
@@ -58,7 +58,7 @@ public int FindMethod(Type type, string methodName, bool staticAccess, ref Expre
{
if (_parsingConfig.CustomTypeProvider.GetExtensionMethods().TryGetValue(t, out var extensionMethodsOfType))
{
- methods.AddRange(extensionMethodsOfType.Where(m => m.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase) && !m.IsGenericMethod));
+ methods.AddRange(extensionMethodsOfType.Where(m => m.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase)));
}
}
diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
index 2b00d294..1fa67b45 100644
--- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq.Dynamic.Core.CustomTypeProviders;
using System.Linq.Dynamic.Core.Exceptions;
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
@@ -1070,7 +1070,7 @@ public void DynamicExpressionParser_ParseLambda_Operator_Less_Greater_With_Guids
// Assert
Assert.Equal(anotherId, result);
- }
+ }
[Theory]
[InlineData("c => c.Age == 8", "c => (c.Age == 8)")]
@@ -1239,7 +1239,7 @@ public void DynamicExpressionParser_ParseLambda_String_TrimEnd_0_Parameters()
var @delegate = expression.Compile();
- var result = (bool) @delegate.DynamicInvoke("This is a test ");
+ var result = (bool)@delegate.DynamicInvoke("This is a test ");
// Assert
result.Should().BeTrue();
@@ -1258,5 +1258,36 @@ public void DynamicExpressionParser_ParseLambda_String_TrimEnd_1_Parameter()
// Assert
result.Should().BeTrue();
}
+
+ public class DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider
+ {
+ public override HashSet GetCustomTypes() => new HashSet(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) };
+ }
+
+ [Fact]
+ public void DynamicExpressionParser_ParseLambda_GenericExtensionMethod()
+ {
+ // Arrange
+ var testList = User.GenerateSampleModels(51);
+ var config = new ParsingConfig()
+ {
+ CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod()
+ };
+
+ // Act
+ string query = "x => MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User4\" || MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User2\"";
+ var expression = DynamicExpressionParser.ParseLambda(config, false, query);
+ var del = expression.Compile();
+
+ var result = Enumerable.Where(testList, del);
+
+
+ var expected = testList.Where(x => new string[] { "User4", "User2" }.Contains(x.UserName)).ToList();
+
+ // Assert
+ Check.That(result).IsNotNull();
+ Check.That(result).HasSize(expected.Count);
+ Check.That(result).Equals(expected);
+ }
}
}
diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
index ecbd1089..aa54a7e0 100644
--- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq.Dynamic.Core.Exceptions;
@@ -1304,6 +1304,69 @@ public void ExpressionTests_Method_OneParam_With_it()
Assert.Equal(expected.Count(), result.Count());
}
+ public class DefaultDynamicLinqCustomTypeProviderForStaticTesting : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider
+ {
+ public override HashSet GetCustomTypes() => new HashSet(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) };
+ }
+
+ [Fact]
+ public void ExpressionTests_MethodCall_GenericStatic()
+ {
+ var config = new ParsingConfig
+ {
+ CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting()
+ };
+
+ // Arrange
+ var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray();
+
+ // Act
+ var expectedResult = list.Where(x => Methods.StaticGenericMethod(x));
+ var result = list.AsQueryable().Where(config, "Methods.StaticGenericMethod(it)");
+
+ // Assert
+ Assert.Equal(expectedResult.Count(), result.Count());
+ }
+
+ [Fact]
+ public void ExpressionTests_MethodCall_Generic()
+ {
+ var config = new ParsingConfig
+ {
+ CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting()
+ };
+
+ // Arrange
+ var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray();
+
+ // Act
+ var methods = new Methods();
+ var expectedResult = list.Where(x => methods.GenericMethod(x));
+ var result = list.AsQueryable().Where("@0.GenericMethod(it)", methods);
+
+ // Assert
+ Assert.Equal(expectedResult.Count(), result.Count());
+ }
+
+ [Fact]
+ public void ExpressionTests_MethodCall_GenericExtension()
+ {
+ var config = new ParsingConfig
+ {
+ CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting()
+ };
+
+ // Arrange
+ var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray();
+
+ // Act
+ var methods = new Methods();
+ var expectedResult = list.Where(x => MethodsItemExtension.Functions.EfCoreCollate(x.Value, "tlh-KX") == 2);
+ var result = list.AsQueryable().Where(config, "MethodsItemExtension.Functions.EfCoreCollate(it.Value,\"tlh-KX\")==2");
+
+ // Assert
+ Assert.Equal(expectedResult.Count(), result.Count());
+ }
[Fact]
public void ExpressionTests_MethodCall_ValueTypeToValueTypeParameter()
diff --git a/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/Methods.cs b/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/Methods.cs
index 37cb557c..cca9c424 100644
--- a/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/Methods.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/Methods.cs
@@ -1,4 +1,4 @@
-namespace System.Linq.Dynamic.Core.Tests.Helpers.Models
+namespace System.Linq.Dynamic.Core.Tests.Helpers.Models
{
public class Methods
{
@@ -22,5 +22,17 @@ public class Item
{
public int Value { get; set; }
}
+
+ public static bool StaticGenericMethod(T value) => value is Item item && item.Value == 1;
+ public bool GenericMethod(T value) => value is Item item && item.Value == 1;
+
+ }
+
+ public static class MethodsItemExtension
+ {
+ public class DummyFunctions { }
+ public static DummyFunctions Functions => new DummyFunctions();
+
+ public static T EfCoreCollate(this DummyFunctions _, T value, string collation) => value;
}
}