From d6d190a9082093c5187c59fe24a7c94f7864b98e Mon Sep 17 00:00:00 2001 From: arjenvrh Date: Tue, 15 May 2018 07:52:14 +0200 Subject: [PATCH 1/5] Update ExpressionParser.cs --- .../Parser/ExpressionParser.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index 9069630e..5efe0431 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -499,9 +499,21 @@ Expression ParseComparisonOperator() } } + if (!typesAreSameAndImplementCorrectInterface) { - CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), op.Text, ref left, ref right, op.Pos); + if (left.Type.IsClass && right is ConstantExpression && HasImplicitConversion(left.Type, right.Type)) + { + left = Expression.Convert(left, right.Type); + } + else if (right.Type.IsClass && left is ConstantExpression && HasImplicitConversion(right.Type, left.Type)) + { + right = Expression.Convert(right, left.Type); + } + else + { + CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), op.Text, ref left, ref right, op.Pos); + } } } @@ -533,6 +545,13 @@ Expression ParseComparisonOperator() return left; } + private bool HasImplicitConversion(Type baseType, Type targetType) + { + return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType) + .Any(mi => mi.GetParameters().FirstOrDefault()?.ParameterType == baseType); + } + private ConstantExpression ParseEnumToConstantExpression(int pos, Type leftType, ConstantExpression constantExpr) { return Expression.Constant(ParseConstantExpressionToEnum(pos, leftType, constantExpr), leftType); From cdfaa4a0f3224be2886231470731ae319a76bac5 Mon Sep 17 00:00:00 2001 From: arjenvrh Date: Tue, 15 May 2018 08:13:30 +0200 Subject: [PATCH 2/5] Update ExpressionParser.cs --- src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index 5efe0431..fe1e480b 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -502,11 +502,11 @@ Expression ParseComparisonOperator() if (!typesAreSameAndImplementCorrectInterface) { - if (left.Type.IsClass && right is ConstantExpression && HasImplicitConversion(left.Type, right.Type)) + if (left.Type.GetTypeInfo().IsClass && right is ConstantExpression && HasImplicitConversion(left.Type, right.Type)) { left = Expression.Convert(left, right.Type); } - else if (right.Type.IsClass && left is ConstantExpression && HasImplicitConversion(right.Type, left.Type)) + else if (right.Type.GetTypeInfo().IsClass && left is ConstantExpression && HasImplicitConversion(right.Type, left.Type)) { right = Expression.Convert(right, left.Type); } @@ -547,7 +547,7 @@ Expression ParseComparisonOperator() private bool HasImplicitConversion(Type baseType, Type targetType) { - return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static) + return baseType.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType) .Any(mi => mi.GetParameters().FirstOrDefault()?.ParameterType == baseType); } From 5147d58eb58ae6f2a29c08fed53130f3ce33db5c Mon Sep 17 00:00:00 2001 From: arjenvrh Date: Tue, 15 May 2018 08:25:07 +0200 Subject: [PATCH 3/5] Update ExpressionParser.cs --- src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index fe1e480b..a8f742e4 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -547,7 +547,7 @@ Expression ParseComparisonOperator() private bool HasImplicitConversion(Type baseType, Type targetType) { - return baseType.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Static) + return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType) .Any(mi => mi.GetParameters().FirstOrDefault()?.ParameterType == baseType); } From f7b4e89532b21ec7e4574c9a476ff5ee94ab1f2a Mon Sep 17 00:00:00 2001 From: Arjen vanRhijn Date: Tue, 15 May 2018 13:36:39 +0200 Subject: [PATCH 4/5] Unit test for implicit casting --- .../ExpressionTests.cs | 18 ++++++++++++++++++ .../Helpers/Models/User.cs | 4 +++- .../Helpers/Models/UserState.cs | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 test/System.Linq.Dynamic.Core.Tests/Helpers/Models/UserState.cs diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs index af22335b..e5d84e15 100644 --- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs @@ -1385,6 +1385,24 @@ public void ExpressionTests_SkipAndTake() } } + [Fact] + public void ExpressionTests_ImplicitCast() + { + Guid code1 = new Guid("651E08E3-85B1-42D1-80AF-68E28E2B7DA6"); + Guid code2 = new Guid("6451FEB2-3226-41D0-961C-B72B7B5A0157"); + + var samples = User.GenerateSampleModels(3); + samples[0].State = new UserState() { StatusCode = code1, Description = "alive" }; + samples[1].State = new UserState() { StatusCode = code2, Description = "deceased" }; + + string queryString = "State == @0"; + IList result = samples.AsQueryable().Where(queryString, code1).ToList(); + + Assert.Equal(1, result.Count); + Assert.Equal(code1, result[0].State.StatusCode); + Assert.Equal("alive", result[0].State.Description); + } + [Fact] public void ExpressionTests_StringCompare() { diff --git a/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/User.cs b/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/User.cs index c3f5c756..89cb22cd 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/User.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/User.cs @@ -14,6 +14,8 @@ public class User public UserProfile Profile { get; set; } + public UserState State { get; set; } + public List Roles { get; set; } public bool TestMethod1() @@ -62,4 +64,4 @@ public static IList GenerateSampleModels(int total, bool allowNullableProf return list.ToArray(); } } -} \ No newline at end of file +} diff --git a/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/UserState.cs b/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/UserState.cs new file mode 100644 index 00000000..c22f6309 --- /dev/null +++ b/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/UserState.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq.Dynamic.Core.Tests.Helpers.Models +{ + public class UserState + { + public Guid StatusCode { get; set; } + public string Description { get; set; } + + public static implicit operator Guid(UserState state) + => state?.StatusCode ?? Guid.Empty; + } +} From d2c635b40bb563067f92b8dba8bdc3c15c4d037e Mon Sep 17 00:00:00 2001 From: Arjen vanRhijn Date: Wed, 16 May 2018 13:35:51 +0200 Subject: [PATCH 5/5] Unit test for implicit casting --- test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs index e5d84e15..5fd4b004 100644 --- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs @@ -1388,6 +1388,7 @@ public void ExpressionTests_SkipAndTake() [Fact] public void ExpressionTests_ImplicitCast() { + //Arrange Guid code1 = new Guid("651E08E3-85B1-42D1-80AF-68E28E2B7DA6"); Guid code2 = new Guid("6451FEB2-3226-41D0-961C-B72B7B5A0157"); @@ -1395,12 +1396,21 @@ public void ExpressionTests_ImplicitCast() samples[0].State = new UserState() { StatusCode = code1, Description = "alive" }; samples[1].State = new UserState() { StatusCode = code2, Description = "deceased" }; + //Act string queryString = "State == @0"; IList result = samples.AsQueryable().Where(queryString, code1).ToList(); + string queryString2 = "@0 == State"; + IList result2 = samples.AsQueryable().Where(queryString2, code1).ToList(); + + //Assert Assert.Equal(1, result.Count); Assert.Equal(code1, result[0].State.StatusCode); Assert.Equal("alive", result[0].State.Description); + + Assert.Equal(1, result2.Count); + Assert.Equal(code1, result2[0].State.StatusCode); + Assert.Equal("alive", result2[0].State.Description); } [Fact]