Итак, у вас есть Expression<Func<TSource, bool>>
, и вы хотите объединить их в один Expression<Func<TSource, bool>>
, используя функцию AND, чтобы вы могли использовать ее в качестве AsQueryable в фреймворке сущностей.
Было бы неплохо иметь этов стиле LINQ, поэтому мы можем поместить его между конкатенацией операторов Linq.
Давайте создадим некоторые функции расширения.
// A function that takes two Expression<Func<TSource, bool>> and returns the AND expression
static Expression<Func<TSource, bool>> AndAlso<TSource> (
this Expression<Func<TSource, bool>> x,
Expression<Func<TSource, bool>> y)
{
// TODO implement
}
Использование:
Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
Expression<Func<Student, bool>> exprAND = expr1.AndAlso(expr2);
var brummyMaleStudents = dbContext.Students.Where(exprAnd).Select(...);
Давайте реализуем AndAlso
Обычно A будет выглядеть следующим образом:
.Where(student => student.City == "Birmingham" && student.Gender == Gender.Male)
Входной параметр student
используется в качестве ввода для левого выражения и в качестве ввода для правого выражения.Нам нужно что-то, что говорит:
Возьмите один входной параметр типа Student, поместите его в левое выражение и в правое выражение и выполните AND между двумя логическими возвращаемыми значениями.Вернуть логический результат.
Для этого мы создаем класс, производный от System.Linq.Expressions.ExpressionVisitor
.
Этот класс представляет действие: «поместите ученика в выражение и вычислите его».Этот расчет называется «посещение выражения».Ввод выражения - это выражение, результат посещения - другое выражение:
internal class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression oldValue;
private readonly Expression newValue;
public ReplaceExpressionVisitor(ParameterExpression oldValue,
ParameterExpression newValue)
{
this.oldValue = oldValue;
this.newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == this.oldValue)
{ // "my" expression is visited
return this.newValue;
}
else
{ // not my Expression, I don't know how to Visit it, let the base class handle this
return base.Visit(node);
}
}
}
Теперь, когда мы создали посетитель выражения, мы можем реализовать AndAlso:
static Expression<Func<TSource, bool>> AndAlso<TSource>(
this Expression<Func<TSource, bool>> expr1,
Expression<Func<TSource, bool>> expr2)
{
// Create one expression that represent expr1 && expr2
// the input of expr1 is a TSource,
// the input of expr2 is a TSource,
// so the input of expr1 && expr2 is a TSource:
ParameterExpression inputParameter = Expression.Parameter(typeof(TSource));
// Visit the left part of the AND:
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], inputParameter)
var left = leftVisitor.Visit(expr1.Body);
// Visit the right part of the AND:
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
// Combine left and right with a binary expression representing left && right:
var andExpression = Expression.AndAlso(left, right);
// return the lambda expression that takes one Tsource as input and returns the AND:
var lambda = Expression.Lambda<Func<TSource, bool>>(andExpression, new[] {parameter});
return lambda;
}
Использование:
Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
var brummyMaleStudents = dbContext.Students.Where(expr1.AndAlso(expr2));