Kelly Cline
Kelly Cline

Reputation: 2246

Expression Tree Generating UnWanted Invoke

(This was initially title, Expression Tree Compare DateTime with Nullable DateTime, but as the accepted answer shows, that was not the issue at all.)

I am attempting to build an Expression Tree to compare dates in an Entity Framework query. In the sample below, 'result' is an IQueryable that has been passed in to the method, and DateFilter is an object from a UI that has the nullable DateTime fields, FirstDate, SecondDate, and enums, DateType and DateMode.

I have seen many similar questions, and the common thread seems to be to use Expression.Convert to make sure the dates are of the correct type. However, I'm still doing something wrong, because when I get to the end, instead of (System.Nullable'1[System.DateTime]$x.EffectiveDate >= ... my expression has .Invoke(.Constant<TheClassIAmIn i.e., 'this'+<>c__DisplayClass47_0>('this'+<>c__DisplayClass47_0).resultFunc)( $x,...

Here is the complete snippet:

        var changeInfo = Expression.Parameter(typeof(MyEntity), "x");
        var targetDate = Expression.Property(changeInfo, DateFilter.DateType.ToString());

        var dateFilter = Expression.Parameter(typeof(MyDateFilter), "DateFilter");
        var firstDate = Expression.Property(dateFilter, "FirstDate");
        var secondDate = Expression.Property(dateFilter, "SecondDate");

        //  Note that FirstDate, SecondDate, and ActionDate are nullable,
        //  SubmittedDate and EffectiveDate are not.
        var ge = Expression.GreaterThanOrEqual(Expression.Convert(targetDate, firstDate.Type), firstDate);

        var tree =
            Expression.Lambda<Func<MyEntity, MyDateFilter, bool>>
            (ge, changeInfo, dateFilter);

    var resultFunc = tree.Compile();
            result = result.Where(x => resultFunc(x, MyDateFilter));

Upvotes: 0

Views: 55

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205629

The problem has nothing in common with DateTime and Nullable<DateTime> comparison.

The expression you see is this:

Expression<Func<MyEntity, bool>> expr = x => resultFunc(x, MyDateFilter);

which is lambda expression with parameter x invoking resultFunc variable containing delegate of type Func<MyEntity, MyDateFilter, bool>.

You shouldn't introduce the MyDateFilter parameter, which produces incompatible lambda expression, shouldn't compile delegates and emit the invocation call expressions at all. Instead, you should build directly a lambda expression of type Expression<Func<MyEntity, bool>>. The MyDateFilter value will be passed as Expression.Constant:

var changeInfo = Expression.Parameter(typeof(MyEntity), "x");
var targetDate = Expression.Property(changeInfo, DateFilter.DateType.ToString());

// Assuming DateFilter is a field/property/variable of type MyDateFilter    
var dateFilter = Expression.Constant(DateFilter);
var firstDate = Expression.Property(dateFilter, "FirstDate");
var secondDate = Expression.Property(dateFilter, "SecondDate");

var ge = Expression.GreaterThanOrEqual(
    Expression.Convert(targetDate, firstDate.Type),
    firstDate);

var predicate = Expression.Lambda<Func<MyEntity, bool>>(
    ge, changeInfo);

result = result.Where(predicate);

Upvotes: 4

Related Questions