我们可以花点时间创建一个帮助方法,它可以让解决这个问题变得非常简单。如果我们创建一个方法,让我们可以像编写委托一样轻松地编写表达式,这将变得非常容易。我们的Compose 方法将接受一个表达式,另一个接受第一个的输出并将其转换为其他内容,创建一个新的表达式,该表达式可以将第一个输入类型的某些内容转换为第二个的输出:
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
这取决于以下方法来将一个表达式的所有实例替换为另一个:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
现在我们可以非常轻松地创建IsNotNull 转换:
public static Expression<Func<TSource, bool>> IsNotNull<TSource, TKey>(
this Expression<Func<TSource, TKey>> expression)
{
return expression.Compose(key => key != null);
}
至于And-ing 两个表达式一起使用,如果使用 LINQ 查询提供程序,最简单的选择是分别在每个表达式上调用 Where,如果这是一个选项。如果没有,您可以将PrediacteBuilder 到And 或Or 两个表达式一起使用:
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 secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}
}