我正在使用C#(包括Linq)来开发Web应用程序。我编写了一个泛型方法来扩展任何实体的Get方法。但是,当我得到运行时异常' LINQ表达式节点类型' Invoke' LINQ to Entities'不支持当代码执行时。以下是代码:
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));
}
}
有人可以告诉我我做错了吗?
答案 0 :(得分:13)
您正在使用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);
}
}
它依赖于以下方法将一个表达式的所有实例替换为另一个:
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);
}
}
答案 1 :(得分: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);
}
}
答案 2 :(得分:2)
我认为问题出在你的And方法中。您可以使用静态表达式方法
自己创建表达式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);
}