Я пытаюсь создать дерево выражений, которое можно вставить в 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 и нашли решение, которое работает длямне. Этот вопрос получил меня там.Я довольно новичок в построении деревьев выражений, поэтому буду признателен (и приветствую) любые комментарии / ответы с улучшениями или критикой.
// 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);