继续获取LINQ to Entities的异常不支持LINQ表达式节点类型'Invoke'

COB*_*BOL 5 .net c# linq entity-framework

我正在使用C#(包括Linq)来开发Web应用程序.我编写了一个泛型方法来扩展任何实体的Get方法.但是,当我得到运行时异常时,执行代码时,"LINQ to Entities"不支持LINQ表达式节点类型"Invoke".以下是代码:

using System.Linq;
using System.Linq.Expressions;
using LinqKit;

public static class ServiceExtension
{
    public static IEnumerable<T> GetActive<T>(this ICrudService<T> crudService, Expression<Func<T, bool>> where)
        where T : class, IDeletable
    {
        return crudService.Get(where.And(w => !w.IsDeleted));
    }
}
Run Code Online (Sandbox Code Playgroud)

有人可以告诉我我做错了什么吗?

Ser*_*rvy 16

您正在使用LinqKit,它只适用于已AsExpandable()调用它们的可查询对象.这将包装底层查询提供程序并将所有调用Invoke(And在内部使用)转换为查询提供程序将理解的内容.

替代方法是简单地不使用LinqKit,并使用以下版本的PredicateBuilder,它可以和/或谓词表达式,而不依赖于使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

它依赖于以下方法将一个表达式的所有实例替换为另一个:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*ger 5

Servy的答案很棒,对我来说非常有用.我已经接受了它并稍微扩展/改变它,并将它添加到这个以回报一点点.

首先,我将True和False属性重命名为BaseAnd(而不是True)和BaseOr(而不是false).基于我如何使用它们以获得我想要的结果,主要是这对我来说更容易理解.

另外,我添加了两个新的泛型函数:AddToPredicateTypeBasedOnIfAndOrOr,它接受两个ref谓词,一个用于os,一个用于ors,并将在其中一个上添加一个表达式,具体取决于它是否应该是和否.这只是为了减少代码重复,因为我的代码在应用程序运行之前不知道它应该是哪种类型.

CombineOrPreicatesWithAndPredicates接受初始谓词表达式,谓词表达式和谓词表达式,并以sql逻辑方式(和列表)和(或列表)组合它们.这也是为了减少代码重复.

希望这有助于那里的人.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)