由于项目中经常要用到类似db.students.where多条件查询,因此需要拼接查询条件。
网上搜索大部分出自 Dynamically Composing Expression Predicates
code如下
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
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 invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
}
使用代码如下:
var predicate = PredicateBuilder.True<Gift>();
predicate = predicate.And(w => w.DataState == DataStateEnum.正常);
List<Gift> list = db.Gifts.Where(predicate);
但出现异常:LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”
网上继续搜索,其中一种如下:
List<Gift> list = db.Gifts.Where(predicate.Compile());作者文中提到“他在Linq to sql 上运行正常”,没有验证,
这样确实可以正常工作,但predicate.Compile()后就变成了Func<T, bool>类型,
Entity Framework - Func引起的数据库全表查询
使用 Entity Framework 最要小心的性能杀手就是 —— 不正确的查询代码造成的数据库全表查询。
“将Func类型的变量作为参数传给Where方法进行LINQ查询时,Enitity Framework会产生全表查询,将整个数据库表中的数据加载到内存,然后在内存中根据Where中的条件进一步查询。” 经SQL Server Profiler监测确实如此。
继续找解决办法,找到个Combining two expressions (Expression<func>)
有人回答:
//Starting from .net 4.0. There is the ExpressionVistor class which allows you to build expressions that are EF safe.
public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
经测试解决问题