Как составить существующие выражения Linq
Я хочу составить результаты двух выражений Linq. Они существуют в форме
Expression<Func<T, bool>>
Итак, два, которые я хочу создать, по сути являются делегатами по параметру (типа T), которые возвращают логическое значение. Результатом, который я хотел бы написать, была бы логическая оценка булевых. Я бы, вероятно, реализовал его как метод расширения, поэтому мой синтаксис будет примерно таким:
Expression<Func<User, bool>> expression1 = t => t.Name == "steve";
Expression<Func<User, bool>> expression2 = t => t.Age == 28;
Expression<Func<User, bool>> composedExpression = expression1.And(expression2);
И позже в моем коде я хочу оценить составное выражение
var user = new User();
bool evaluated = composedExpression.Compile().Invoke(user);
Я размышлял с несколькими разными идеями, но я боюсь, что это сложнее, чем я надеялся. Как это делается?
Ответы
Ответ 1
Вот пример:
var user1 = new User {Name = "steve", Age = 28};
var user2 = new User {Name = "foobar", Age = 28};
Expression<Func<User, bool>> expression1 = t => t.Name == "steve";
Expression<Func<User, bool>> expression2 = t => t.Age == 28;
var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>());
var result = Expression.Lambda<Func<User, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters);
Console.WriteLine(result.Compile().Invoke(user1)); // true
Console.WriteLine(result.Compile().Invoke(user2)); // false
Вы можете повторно использовать этот код с помощью методов расширения:
class User
{
public string Name { get; set; }
public int Age { get; set; }
}
public static class PredicateExtensions
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1,Expression<Func<T, bool>> expression2)
{
InvocationExpression invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters);
}
}
class Program
{
static void Main(string[] args)
{
var user1 = new User {Name = "steve", Age = 28};
var user2 = new User {Name = "foobar", Age = 28};
Expression<Func<User, bool>> expression1 = t => t.Name == "steve";
Expression<Func<User, bool>> expression2 = t => t.Age == 28;
var result = expression1.And(expression2);
Console.WriteLine(result.Compile().Invoke(user1));
Console.WriteLine(result.Compile().Invoke(user2));
}
}
Ответ 2
Вы также можете использовать LinqKit, который может сделать все это для вас. Посмотрите на эту ссылку
http://www.albahari.com/nutshell/linqkit.aspx
Ответ 3
Почему бы вам просто не использовать Expression.And
и обработать полученный BinaryExpression
?
Expression<Func<T, bool>> expr1 = t => t.Name == "steve";
Expression<Func<T, bool>> expr2 = t => t.Age == 28;
Expression composed = Expression.And(expr1.Body, expr2.Body);
Конечно, вы можете объединить это в лямбда, чтобы получить нужную подпись, но это дорого и нужно делать только один раз, а не несколько раз:
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(
expr, Expression.Parameter(typeof(T), "t")
);
/EDIT: вы можете, конечно, объединить лямбда, как описано ниже, но это связано с избыточными компиляциями и вызовами функций:
Expression<Func<string, bool>> z = t => expr1.Compile()(t) && expr2.Compile()(t);
Черт, время простоя на техническое обслуживание. Пришлось снова напечатать весь пост.: -/
/EDIT: aku right, хотя. Вы должны вызывать expr2
отдельно, иначе компилятор не найдет ссылку на параметр.