Search code examples
c#linqentity-frameworklinq-to-entities

Error "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities" in where clause inside the method


When I execute my query:

rs.Select(x => x.id).ToArray();

I obtain this error:

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities

This is the method that generates the error (probably func(x)):

public IQueryable<TEntity> Compare<TEntity>(IQueryable<TEntity> source, Func<TEntity, int> func)
{
     IQueryable<TEntity> res = source;

     if (!this.LBoundIsNull) res = res.Where(x => func(x) >= _lBound);
     if (!this.UBoundIsNull) res = res.Where(x => func(x) <= _uBound);

     return res;
}

I invoke the method in this mode:

Document doc = new Document();
doc.Number = new RangeValues(lBound, null);

using (MyEntities db = new MyEntities())
{
    var rs = db.documents;
    if (doc.Number != null) rs = doc.Numero.Compare(rs, x => x.number);

    long[] id = rs.Select(x => x.id).ToArray();
}

What is wrong?


Solution

  • To do what you want you will need to do something like:

    public static IQueryable<TEntity> Compare<TEntity>(IQueryable<TEntity> source, Expression<Func<TEntity, int>> func)
    {
        IQueryable<TEntity> res = source;
    
        if (!LBoundIsNull) 
        {
            Expression ge = Expression.GreaterThanOrEqual(func.Body, Expression.Constant(_lBound));
            var lambda = Expression.Lambda<Func<TEntity, bool>>(ge, func.Parameters);
            res = res.Where(lambda);
        }
    
        if (!UBoundIsNull)
        {
            Expression le = Expression.LessThanOrEqual(func.Body, Expression.Constant(_uBound));
            var lambda = Expression.Lambda<Func<TEntity, bool>>(le, func.Parameters);
            res = res.Where(lambda);
        }
    
        return res;
    }
    

    As you can see you'll need to do some expression-tree plumbing. You call the method in the same way as before.

    Now... is it really possible to use LinqKit as suggested by @jbl? Yes... By shaking a little the magic wand...

    using LinqKit;
    
    public static IQueryable<TEntity> Compare<TEntity>(IQueryable<TEntity> source, Expression<Func<TEntity, int>> func)
    {
        IQueryable<TEntity> res = source;
    
        if (!LBoundIsNull)
        {
            Expression<Func<TEntity, bool>> lambda = x => func.Invoke(x) >= _lBound;
            res = res.Where(lambda.Expand());
        }
    
        if (!UBoundIsNull)
        {
            Expression<Func<TEntity, bool>> lambda = x => func.Invoke(x) <= _uBound;
            res = res.Where(lambda.Expand());
        }
    
        return res;
    }
    

    Note the use of the Invoke() and Expand() LinqKit methods.