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

前端 未结 3 2113
清歌不尽
清歌不尽 2020-12-01 13:45

I am using C# (including Linq) to develop a web application. I have written a generic method to extend Get method of any entity. However when I get the runtime exception \'T

相关标签:
3条回答
  • 2020-12-01 13:52

    You're using LinqKit, which will only work on queryables that have had AsExpandable() called on them. This will wrap the underlying query provider and translate all calls to Invoke (which And is using internally) into something that the query provider will understand.

    The alternative would be to simply not use LinqKit, and use the following version of PredicateBuilder that can And/Or predicate expressions without relying on the use of Invoke:

    public static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> True<T>() { return f => true; }
        public static Expression<Func<T, bool>> False<T>() { return f => false; }
    
        public static Expression<Func<T, bool>> Or<T>(
            this Expression<Func<T, bool>> expr1,
            Expression<Func<T, bool>> expr2)
        {
            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
            return Expression.Lambda<Func<T, bool>>
                  (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
        }
    
        public static Expression<Func<T, bool>> And<T>(
            this Expression<Func<T, bool>> expr1,
            Expression<Func<T, bool>> expr2)
        {
            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
            return Expression.Lambda<Func<T, bool>>
                  (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
        }
    }
    

    It instead relies on the following method to replace all instance of one expression with another:

    public static Expression Replace(this Expression expression,
        Expression searchEx, Expression replaceEx)
    {
        return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
    }
    
    internal class ReplaceVisitor : ExpressionVisitor
    {
        private readonly Expression from, to;
        public ReplaceVisitor(Expression from, Expression to)
        {
            this.from = from;
            this.to = to;
        }
        public override Expression Visit(Expression node)
        {
            return node == from ? to : base.Visit(node);
        }
    }
    
    0 讨论(0)
  • 2020-12-01 13:53

    Servy's answer is great and has been very useful for me. I've taken it and expanded/changed it slightly, and will add it onto this to pay back a little bit.

    First off, I renamed the True and False properties to be BaseAnd (instead of True) and BaseOr (instead of false). Mainly this was to be more understandable to me based on how I use them in order to get the results I desire.

    In addition, I added two new generic functions: AddToPredicateTypeBasedOnIfAndOrOr, which takes two ref predicates, one for ands and one for ors and will add an expression onto one of them depending on if it's supposed to be an and or not. This is just to reduce code duplication as my code doesn't know which type it's supposed to be before the app runs.

    CombineOrPreicatesWithAndPredicates takes an initial predicate expression, an and predicate expression and an or predicate expression and combine them in a sql logical way, (and list) and (or list). This is also to reduce code duplication.

    Hope this helps someone out there.

    public static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> BaseAnd<T>() { return f => true; }
        public static Expression<Func<T, bool>> BaseOr<T>() { return f => false; }
    
        public static Expression<Func<T, bool>> Or<T>(
            this Expression<Func<T, bool>> expr1,
            Expression<Func<T, bool>> expr2)
        {
            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
            return Expression.Lambda<Func<T, bool>>
                  (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
        }
    
        public static Expression<Func<T, bool>> And<T>(
            this Expression<Func<T, bool>> expr1,
            Expression<Func<T, bool>> expr2)
        {
            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
            return Expression.Lambda<Func<T, bool>>
                  (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
        }
    
        public static Expression Replace(this Expression expression,
        Expression searchEx, Expression replaceEx)
        {
            return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
        }
    
        public static Expression<Func<T, bool>> CombineOrPreicatesWithAndPredicates<T>(this Expression<Func<T, bool>> combinedPredicate,
            Expression<Func<T, bool>> andPredicate, Expression<Func<T, bool>> orPredicate)
        {
            combinedPredicate = combinedPredicate ?? BaseAnd<T>();
            if (andPredicate != null && orPredicate!=null)
            {
                andPredicate = andPredicate.And(orPredicate);
                combinedPredicate = combinedPredicate.And(andPredicate);
            }
            else if (orPredicate!=null)
            {
                combinedPredicate = combinedPredicate.And(orPredicate);
            }
            else
            {
                combinedPredicate = combinedPredicate.And(andPredicate);
            }
            return combinedPredicate;
        }
    
        public static void AddToPredicateTypeBasedOnIfAndOrOr<T>(ref Expression<Func<T, bool>> andPredicate,
            ref Expression<Func<T, bool>> orPredicate, Expression<Func<T, bool>> newExpression, bool isAnd)
        {
            if (isAnd)
            {
                andPredicate = andPredicate ?? BaseAnd<T>();
                andPredicate = andPredicate.And(newExpression);
            }
            else
            {
                orPredicate = orPredicate ?? BaseOr<T>();
                orPredicate = orPredicate.Or(newExpression);
            }
        }
    }
    
    internal class ReplaceVisitor : ExpressionVisitor
    {
        private readonly Expression from, to;
    
        public ReplaceVisitor(Expression from, Expression to)
        {
            this.from = from;
            this.to = to;
        }
    
        public override Expression Visit(Expression node)
        {
            return node == from ? to : base.Visit(node);
        }
    }
    
    0 讨论(0)
  • 2020-12-01 14:04

    I beleive the problem is in your And method. You can create the expression yourself using the static Expression methods

    public static IEnumerable<T> GetActive<T>(this ICrudService<T> crudService, Expression<Func<T, bool>> where)
            where T : class, IDeletable
        {
    
    
        var parameter = where.Parameters.FirstOrDefault();
        var property = Expression.PropertyOrField(parameter, "IsDeleted");
        var notProperty = Expression.Not(property);
        var andExpression = Expression.AndAlso(where.Body, notProperty);
    
        var lambda = Expression.Lambda<Func<T, bool>>(andExpression, parameter);
    
    
    
        return crudService.Get(lambda);
    }
    
    0 讨论(0)
提交回复
热议问题