【发布时间】:2011-04-12 06:11:08
【问题描述】:
我正在尝试构建一个表达式树,我可以将它输入到 Linq2SQL 中,以便它生成一个干净整洁的查询。我的目的是构建一个过滤器,将任意一组单词与 AND 和 NOT(或 OR 和 NOT)结合在一起。因为我想改变我搜索的字段,所以我最好通过调用各种辅助函数来组合Expresssion<Func<T, string, bool>> 的列表(其中 T 是我正在操作的实体)。然后我会收到一个单词数组并循环遍历它们并构建一个 Expresssion<Func<T, bool>> up(必要时否定某些表达式),我最终可以将其提供给 .Where 语句。
我一直在使用 LINQKit PredicateBuilder,但这段代码处理的是单参数表达式。但是,它为我自己的尝试提供了一些基础。我的目标是做这样的事情:
var e = (Expression<Func<Entity, string, bool>>)((p, w) => p.SomeField.ToLower().Contains(w));
var words = new []{"amanda", "bob"};
var expr = (Expression<Func<Entity, bool>>)(p => false);
// building up an OR query
foreach(var w in words) {
var w1 = w;
>>>>expr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(expr.Body, (Expression<Func<Entity, bool>>)(p => e(p, w))));
}
var filteredEntities = table.Where(expr);
但由于我使用的是表达式,所以由 >>>> 标记的行显然是非法的(不能像我对函数那样做 e(p, w))。所以我的问题是如何将单个变量(单词)部分应用于包含具有多个参数的函数的表达式?
好的,在 LINQPad 中摆弄了一下,找到了适合我的解决方案。 This question 把我带到了那里。我对构建表达式树还很陌生,所以我会感谢(并赞成)任何有改进或批评的 cmets/答案。
// Some set of expressions to test against
var expressions = new List<Expression<Func<Entity, string, bool>>>();
expressions.Add((p, w) => p.FirstName.ToLower().Contains(w));
expressions.Add((p, w) => p.LastName.ToLower().Contains(w));
expressions.Add((p, w) => p.Department != null && p.Department.Name.ToLower().Contains(w));
var words = new []{"amanda", "bob"};
var negs = new []{"smith"}; // exclude any entries including these words
var isAndQuery = true; // negate for an OR query
Expression<Func<Entity, bool>> posExpr = p => isAndQuery;
var entityParameter = Expression.Parameter(typeof(Entity), null);
// Build up the NOTs
var negExpr = (Expression<Func<Entity, bool>>)(p => true);
foreach(var w in negs) {
var w1 = w;
foreach(var e in expressions) {
var andNot = Expression.Invoke(e, entityParameter, Expression.Constant(w1));
negExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(negExpr.Body, Expression.Not(andNot)), entityParameter);
}
}
// Build up the ANDs or ORs
foreach(var w in words) {
var w1 = w;
var orExpr = (Expression<Func<Entity, bool>>)(p => false);
foreach(var e in expressions) {
var orElse = Expression.Invoke(e, entityParameter, Expression.Constant(w1));
orExpr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(orExpr.Body, orElse), entityParameter);
}
var orInvoked = Expression.Invoke(orExpr, posExpr.Parameters.Cast<Expression>());
if(isAndQuery)
posExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(posExpr.Body, orInvoked), entityParameter);
else
posExpr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(posExpr.Body, orInvoked), entityParameter);
}
var posInvoked = Expression.Invoke(posExpr, posExpr.Parameters.Cast<Expression>());
var finalExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(negExpr.Body, posInvoked), entityParameter);
var filteredEntities = entities.Where(finalExpr);
【问题讨论】:
-
我确定您已经看过stackoverflow.com/questions/3657843/…,但以防万一您还没有。
标签: c# linq tsql expression-trees currying