zzzprojects / System.Linq.Dynamic.Core

The .NET Standard / .NET Core version from the System Linq Dynamic functionality.
https://dynamic-linq.net/
Apache License 2.0
1.55k stars 228 forks source link

Value typed property from subquery are casted as `object` #775

Closed neilbgr closed 6 months ago

neilbgr commented 7 months ago

All value typed properties read from subquery are casted as object and can not be compared with other same typed value. Even trying with WrappedConstant().

Operator '!=' incompatible with operand types 'Boolean' and 'Object' (at index 6)
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.CheckAndPromoteOperands(Type signatures, TokenId opId, String opName, Expression& left, Expression& right, Int32 errorPos) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 2341
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseComparisonOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 613
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLogicalAndOrOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 414
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIn() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 325
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAndOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 308
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseOrOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 290
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLambdaOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 270
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNullCoalescingOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 257
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseConditionalOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 241
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.Parse(Type resultType, Boolean createParameterCtor) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 156
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type delegateType, ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 121
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 98
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, Type itType, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 357
   at System.Linq.Dynamic.Core.DynamicQueryableExtensions.Where(IQueryable source, ParsingConfig config, String predicate, Object[] args) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicQueryableExtensions.cs:line 2656
   at System.Linq.Dynamic.Core.DynamicQueryableExtensions.Where[TSource](IQueryable`1 source, ParsingConfig config, String predicate, Object[] args) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicQueryableExtensions.cs:line 2623
   at System.Linq.Dynamic.Core.DynamicQueryableExtensions.Where[TSource](IQueryable`1 source, String predicate, Object[] args) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicQueryableExtensions.cs:line 2629

Fiddle that reproduce the issue: https://dotnetfiddle.net/slGSSp

Thanks

neilbgr commented 7 months ago

If adding this condition after the else, my case is fixed but 2 unit tests failed...

From System.Linq.Dynamic.Core\Parser\SupportedMethods\MethodFinder.cs line 132:

  for (var i = 0; i < args.Length; i++)
  {
      if (args[i].Type != methodData.Args[i].Type &&
          args[i].Type.IsArray && methodData.Args[i].Type.IsArray &&
          args[i].Type != typeof(string) && methodData.Args[i].Type == typeof(object[]))
      {
          args[i] = _expressionHelper.ConvertAnyArrayToObjectArray(args[i]);
      }
      else if (methodData.Args[i].Type != typeof(object)) // Fix (or hack?)
      {
          args[i] = methodData.Args[i];
      }
  }

image

neilbgr commented 7 months ago

My unit test (in file test\System.Linq.Dynamic.Core.Tests\QueryableTests.UseParameterizedNamesInDynamicQuery.cs):

public void Issue775()
{
    var list = new List<Customer>
    {
        new()
        {
            CustomerID = 1,
            Name = "Duffy",
            GenderType = Gender.Female
        },
        new()
        {
            CustomerID = 2,
            Name = "Garry",
            GenderType = Gender.Male
        },
        new()
        {
            CustomerID = 3,
            Name = "John",
            GenderType = Gender.Male
        }
    };

    var config = new ParsingConfig
    {
        UseParameterizedNamesInDynamicQuery = true
    };

    var subquery = list.AsQueryable().Where(_ => _.CustomerID == 2);
    //var subquery = list.AsQueryable().Where("CustomerID == 2");

    // Act
    var result = list.AsQueryable().Where(config, "CustomerID != @0.Select(CustomerID).FirstOrDefault()", subquery).ToArray();

    // Assert
    result.Should().HaveCount(2);
}
StefH commented 7 months ago

I was just also debugging this part.

The root cause is that object is detected from that IEnnumerable helper class.

StefH commented 7 months ago

https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/777