所以我们可以在这里做的是创建一个Compose 方法,它接受一个Expression 代表一个带有一个参数和一个返回值的 lambda,然后是第二个 lambda,它将第一个 lambda 的输出作为输入,然后返回一个新的 lambda,它接受第一个参数的输入并返回第二个参数的输出。
如果它只是普通的委托,则该方法看起来像这样(只是为了让您了解它在概念上所做的事情):
public static Func<TFirst, TResult> Compose<TFirst, TIntermediate, TResult>(
Func<TFirst, TIntermediate> first,
Func<TIntermediate, TResult> second)
{
return firstParam => second(first(firstParam));
}
这在Expression 对象中实现,有点复杂:
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);
}
这使用以下方法将一个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);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
现在我们有了Compose 方法,我们可以编写一个表达式,它接受你的一个项目并返回一个Contract,然后编写另一个方法来接受该合同并计算一个bool 指示它是否有效:
public static Expression<Func<Item, bool>> GetFilter(
TimeSpan fromValue, TimeSpan toValue)
{
Expression<Func<Item, Contract>> currentContract =
q => q.StaffContracts
.OrderByDescending(p => p.SignedDate)
.Where(p => p.Active)
.FirstOrDefault();
return currentContract.Compose(contract =>
contract != null &&
contract.TimeSpan >= fromValue &&
contract.TimeSpan <= toValue);
}
这里的 Compose 方法将在内部将第二个 lambda 中的所有 contract 实例替换为 currentContract 的主体。所以效果是如果你把它写了三遍,即使这样你就不需要这样做了。
这个Compose 方法是您可以随时在表达式树中创建变量时使用的东西(查询提供程序不支持的东西)。您始终可以创建一个计算变量值的方法,然后 Compose 在另一个表达式中使用它。