PredicateBuilder ArgumentException

Posts   
 
    
TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 05-Dec-2016 00:39:03   

This in LinqPad against Northwind:

var predicate = PredicateBuilder.Null<EmployeeEntity>();
predicate = predicate.Or(c => c.BirthDate < DateTime.Now);
predicate = predicate.Or(c => c.HireDate == null);
var q = Employee.Where(predicate);
q.Dump();

throws "Argument types do not match"ArgumentException

 at System.Linq.Expressions.Expression.Constant(Object value, Type type)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.InMemoryEvalCandidateFinder.Wrap(Expression toWrap)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.InMemoryEvalCandidateFinder.HandleExpression(Expression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleBinaryExpression(BinaryExpression expressionToHandle)
...

swap the order and it works fine

var predicate = PredicateBuilder.Null<EmployeeEntity>();
predicate = predicate.Or(c => c.HireDate == null);
predicate = predicate.Or(c => c.BirthDate < DateTime.Now);
var q = Employee.Where(predicate);
q.Dump();

V5.0.7

Jeremy Thomas
daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 05-Dec-2016 08:19:33   

Reproduced with RTL v5.0.8.

Works

TestMethod]
public void PredicateBuilderOrder()
{
    var predicate = PredicateBuilder.Null<EmployeeEntity>();
    predicate = predicate.Or(c => c.HireDate == null);
    predicate = predicate.Or(c => c.BirthDate < DateTime.Now);
                
            
    using (var adapter = new DataAccessAdapter())
    {
        var metaData = new LinqMetaData(adapter);
        var q = metaData.Employee.Where(predicate);

        var results = q.ToList();
    }           
}

Expression in the trace log:

value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2`1[NW.LLBL.MSSQL.Adapter.v50.EntityClasses.EmployeeEntity]).Where(c => ((c.HireDate == null) OrElse (Convert(c.BirthDate) < Convert(Convert(DateTime.Now)))))

Fails

TestMethod]
public void PredicateBuilderOrder()
{
    var predicate = PredicateBuilder.Null<EmployeeEntity>();
    predicate = predicate.Or(c => c.BirthDate < DateTime.Now);  
    predicate = predicate.Or(c => c.HireDate == null);
                                
    using (var adapter = new DataAccessAdapter())
    {
        var metaData = new LinqMetaData(adapter);
        var q = metaData.Employee.Where(predicate);

        var results = q.ToList();
    }           
}

Expression in the trace log:

value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2`1[NW.LLBL.MSSQL.Adapter.v50.EntityClasses.EmployeeEntity]).Where(c => ((c.BirthDate < Convert(DateTime.Now)) OrElse (Convert(c.HireDate) == Convert(null))))

The difference and the cause of error seems to be in **Convert(null) **. We will look into it.

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 05-Dec-2016 11:51:49   

Strange phenomenon simple_smile The Convert(null) is apparently inserted through predicate builder although it has no code doing so. A normal linq query doesn't insert this... Looking into a fix..

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 05-Dec-2016 11:58:22   

Casting the null to (DateTime?) works:

var predicate = PredicateBuilder.Null<EmployeeEntity>();
predicate = predicate.Or(c => c.BirthDate < DateTime.Now);
predicate = predicate.Or(c => c.HireDate == (DateTime?)null);

Does that work for you? Or is this complicated to do as you generate these expressions in a toolkit? The problem is caused by the ConvertExpression, which doesn't have the type of the other side as the type, but simply object as its value is 'null'. Casting will solve it as it will wrap the Convert(null) into a Convert() to DateTime? which will match the type of the other side.

Problem with predicatebuilder is... there's almost no code, so I don't know what exactly causes this...

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 07-Dec-2016 10:23:06   

Yep that works for me, though actually since I made the post my real code has evolved to this


      var occPredicate = PredicateBuilder.Null<T>();
      var predicateNullDates = PredicateBuilder.Null<T>();

...

          if (!dateFrom.IsNullOrEmpty())
            occPredicate = occPredicate.And(oc => oc.DateNotifiedToAuthority >= dateFrom);
          if (!dateTo.IsNullOrEmpty())
          {
            var dateTotransformedForInclusiveSearch = dateTo.TransformForInclusiveToSearch();
            occPredicate = occPredicate.And(oc => oc.DateNotifiedToAuthority < dateTotransformedForInclusiveSearch);
          }
          if (includeNullDates)
            predicateNullDates = predicateNullDates.And(oc => oc.DateNotifiedToAuthority == null);
          return occPredicate == null ? occurrenceQuery : occurrenceQuery.Where(predicateNullDates.Or(occPredicate));

With the two PredicateBuilders it doesn't require the cast. The intent of the code is: if at least one date filter is specified as well as the option to include null dates then OR the date range with NULL.

So all good, but still weird that it fails in my first example

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 07-Dec-2016 10:50:49   

Please let us know if you run into a situation where these can't be worked around. I've also added a workitem to look into this in 5.2 as it might be we can add some code to the linq provider to intercept the Convert(null) and deal with it.

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 03-Jan-2023 12:16:27   

Otis wrote:

Please let us know if you run into a situation where these can't be worked around. I've also added a workitem to look into this in 5.2 as it might be we can add some code to the linq provider to intercept the Convert(null) and deal with it.

Happy New Year, that moment has finally come, hit this using Dynamic Linq.

Hope you can do something your end.

Jeremy Thomas
Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 04-Jan-2023 07:01:16   

Hi Tom, it has been 4+ years since the original post. Could you please provide the LLBLGen runtime library version and a code snippet?

TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 09-Jan-2023 01:10:22   

Code Snippet is pretty much the same as before but using the DynamicExpressionParser

var predicate = PredicateBuilder.Null<EmployeeEntity>();
var birthDateExpression = DynamicExpressionParser.ParseLambda<EmployeeEntity, bool>(null, true, "BirthDate < @0", DateTime.Now);
predicate = predicate.Or(birthDateExpression);
var hireDateExpression = DynamicExpressionParser.ParseLambda<EmployeeEntity, bool>(null, true, "HireDate < null");
predicate = predicate.Or(hireDateExpression);
var q = Employee.Where(predicate);
q.Dump();

LLBL version 5.9

<NuGetReference>System.Linq.Dynamic.Core</NuGetReference>

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 09-Jan-2023 11:08:41   

Please give a snippet that doesn't use external classes we don't provide. I don't know what DynamicExpressionParser is. Does it produce the same issue without that class?

Looking at this, the predicatebuilder is a class that's provided as-is but originated elsewhere (Linqpad I think), but it's unclear how it introduces this issue in the first place as described earlier in this thread. It's not simple to fix this as well, as it requires a specific visitor to find the particular expression tree and the problem only occurs with a predicate builder.

If it only occurs now with the 'dynamicexpressionparser', I'm sorry but it might be better to choose an alternative way to formulate the query, e.g. using queryspec.

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 09-Jan-2023 12:06:29   

https://dynamic-linq.net/ provides a way to execute string LINQ queries, we use it all the time. The strongly typed version in my original post has the same error, the difference being the work-around for the strongly typed version is not available in dynamic Linq

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 09-Jan-2023 15:14:27   

So I took a look again at where it fails exactly. The initial analysis was ok but missed an important detail: it's actually trivial to fix it! smile

The in-memory evaluator which currently chokes on the Convert(null) expression can simply return it and it's handled properly. So I added a special check for this at that location and it works now without the work around needed.

Hotfix build 5.9.4 should have this fix. Could you test that in your query with the dynamic linq code?

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 10-Jan-2023 11:32:34   

Yep got it!smile

Thankyou!thumbsup

Jeremy Thomas