diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
index 05b26969..7b0167d8 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
@@ -378,6 +378,26 @@ public Expression ConvertAnyArrayToObjectArray(Expression arrayExpression)
);
}
+ ///
+ public bool TryConvertTypes(ref Expression left, ref Expression right)
+ {
+ if (!_parsingConfig.ConvertObjectToSupportComparison || left.Type == right.Type || Constants.IsNull(left) || Constants.IsNull(right))
+ {
+ return false;
+ }
+
+ if (left.Type == typeof(object))
+ {
+ left = Expression.Convert(left, right.Type);
+ }
+ else if (right.Type == typeof(object))
+ {
+ right = Expression.Convert(right, left.Type);
+ }
+
+ return true;
+ }
+
private Expression? GetMemberExpression(Expression? expression)
{
if (ExpressionQualifiesForNullPropagation(expression))
@@ -455,26 +475,6 @@ private List CollectExpressions(bool addSelf, Expression sourceExpre
return list;
}
- ///
- /// If the types are different (and not null), try to convert the object type to other type.
- ///
- private void TryConvertTypes(ref Expression left, ref Expression right)
- {
- if (!_parsingConfig.ConvertObjectToSupportComparison || left.Type == right.Type || Constants.IsNull(left) || Constants.IsNull(right))
- {
- return;
- }
-
- if (left.Type == typeof(object))
- {
- left = Expression.Convert(left, right.Type);
- }
- else if (right.Type == typeof(object))
- {
- right = Expression.Convert(right, left.Type);
- }
- }
-
private static Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
{
if (!TryGetStaticMethod(methodName, left, right, out var methodInfo))
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index eba7fffc..a43b534e 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -527,7 +527,11 @@ private Expression ParseComparisonOperator()
// If left or right is NullLiteral, just continue. Else check if the types differ.
if (!(Constants.IsNull(left) || Constants.IsNull(right)) && left.Type != right.Type)
{
- if (left.Type.IsAssignableFrom(right.Type) || HasImplicitConversion(right.Type, left.Type))
+ if ((left.Type == typeof(object) || right.Type == typeof(object)) && _expressionHelper.TryConvertTypes(ref left, ref right))
+ {
+ // #937
+ }
+ else if (left.Type.IsAssignableFrom(right.Type) || HasImplicitConversion(right.Type, left.Type))
{
right = Expression.Convert(right, left.Type);
}
@@ -2551,7 +2555,7 @@ private bool TokenIsIdentifier(string id)
{
return _textParser.TokenIsIdentifier(id);
}
-
+
private string GetIdentifier()
{
_textParser.ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
diff --git a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
index ce4b902e..bbc691cd 100644
--- a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
@@ -48,4 +48,9 @@ internal interface IExpressionHelper
Expression GenerateDefaultExpression(Type type);
Expression ConvertAnyArrayToObjectArray(Expression arrayExpression);
+
+ ///
+ /// If the types are different (and not null), try to convert the object type to other type.
+ ///
+ public bool TryConvertTypes(ref Expression left, ref Expression right);
}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
index 22d22a72..23f6a963 100644
--- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
@@ -5,6 +5,8 @@
using System.Linq.Dynamic.Core.Tests.Helpers.Entities;
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
using System.Linq.Expressions;
+using System.Text;
+using Docker.DotNet.Models;
using FluentAssertions;
using Xunit;
@@ -326,6 +328,56 @@ public void Where_Dynamic_DateTimeConstructor_Issue662()
result2.Should().HaveCount(1);
}
+ // #937
+ [Theory]
+ [InlineData("NameCalculated == \"FooFoo\"", 1)]
+ [InlineData("\"FooFoo\" == NameCalculated", 1)]
+ [InlineData("NameCalculated == \"x\"", 0)]
+ [InlineData("NameCalculated != \"x\"", 2)]
+ [InlineData("NameCalculated <> \"x\"", 2)]
+ [InlineData("\"x\" == NameCalculated", 0)]
+ [InlineData("\"x\" != NameCalculated", 2)]
+ [InlineData("\"x\" <> NameCalculated", 2)]
+ public void Where_Dynamic_CompareObjectToString_ConvertObjectToSupportComparisonIsTrue(string expression, int expectedCount)
+ {
+ // Arrange
+ var config = new ParsingConfig
+ {
+ ConvertObjectToSupportComparison = true
+ };
+ var queryable = new[]
+ {
+ new PersonWithObject { Name = "Foo", DateOfBirth = DateTime.UtcNow.AddYears(-31) },
+ new PersonWithObject { Name = "Bar", DateOfBirth = DateTime.UtcNow.AddYears(-1) }
+ }.AsQueryable();
+
+ // Act
+ queryable.Where(config, expression).ToList().Should().HaveCount(expectedCount);
+ }
+
+ // #937
+ [Theory]
+ [InlineData("NameCalculated == \"FooFoo\"", 0)] // This is the expected behavior when ConvertObjectToSupportComparison is false because "Foo" is a string and NameCalculated is an object which is a calculated string.
+ [InlineData("\"FooFoo\" == NameCalculated", 0)] // Also expected.
+ [InlineData("NameCalculated == \"x\"", 0)]
+ [InlineData("NameCalculated != \"x\"", 2)]
+ [InlineData("NameCalculated <> \"x\"", 2)]
+ [InlineData("\"x\" == NameCalculated", 0)]
+ [InlineData("\"x\" != NameCalculated", 2)]
+ [InlineData("\"x\" <> NameCalculated", 2)]
+ public void Where_Dynamic_CompareObjectToString_ConvertObjectToSupportComparisonIsFalse(string expression, int expectedCount)
+ {
+ // Arrange
+ var queryable = new[]
+ {
+ new PersonWithObject { Name = "Foo", DateOfBirth = DateTime.UtcNow.AddYears(-31) },
+ new PersonWithObject { Name = "Bar", DateOfBirth = DateTime.UtcNow.AddYears(-1) }
+ }.AsQueryable();
+
+ // Act
+ queryable.Where(expression).ToList().Should().HaveCount(expectedCount);
+ }
+
// #451
[Theory]
[InlineData("Age == 99", 0)]
@@ -448,7 +500,11 @@ private class PersonWithObject
{
// Deliberately typing these as `object` to illustrate the issue
public object? Name { get; set; }
+
+ public object? NameCalculated => Name + Encoding.ASCII.GetString(Convert.FromBase64String("Rm9v")); // "...Foo";
+
public object Age => Convert.ToInt32(Math.Floor((DateTime.Today.Month - DateOfBirth.Month + 12 * DateTime.Today.Year - 12 * DateOfBirth.Year) / 12d));
+
public DateTime DateOfBirth { get; set; }
}