您需要将 Func<T, DateTime> 参数更改为 Expression<Func<T, DateTime>> 并将它们合并到所需的表达式中。
不幸的是,C# 编译器和 BCL 都不能帮助完成后面的任务(来自其他表达式的表达式组合)。有一些 3rd 方包,如 LinqKit、NeinLinq 等可以解决这个问题,所以如果您打算大量使用表达式组合,您可以考虑使用这些库之一。
但原理是一样的。在某些时候,自定义ExpressionVisitor 用于将原始表达式的一部分替换为另一个表达式。例如,我在这种简单的场景中使用的是创建编译时 lambda 表达式,其中包含用作占位符的附加参数,然后用与 string.Replace 几乎相同的方式替换为实际表达式。
为此,我使用以下辅助方法将 lambda 表达式参数替换为另一个表达式:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : base.VisitParameter(node);
}
}
而有问题的方法可能是这样的:
public static Expression<Func<T, bool>> IsPeriodActive<T>(
DateTime checkPeriodStart,
DateTime checkPeriodEnd,
Expression<Func<T, DateTime>> entityPeriodStart,
Expression<Func<T, DateTime>> entityPeriodEnd)
{
var entityParam = Expression.Parameter(typeof(T), "entity");
var periodStartValue = entityPeriodStart.Body
.ReplaceParameter(entityPeriodStart.Parameters[0], entityParam);
var periodEndValue = entityPeriodEnd.Body
.ReplaceParameter(entityPeriodEnd.Parameters[0], entityParam);
Expression<Func<DateTime, DateTime, bool>> baseExpr = (periodStart, periodEnd) =>
(checkPeriodEnd >= periodStart && checkPeriodEnd <= periodEnd)
|| (checkPeriodStart >= periodStart && checkPeriodEnd <= periodEnd)
|| (periodStart >= checkPeriodStart && periodStart <= checkPeriodEnd)
|| (periodEnd >= checkPeriodStart && periodEnd <= checkPeriodEnd)
|| (periodStart >= checkPeriodStart && periodStart <= checkPeriodEnd);
var periodStartParam = baseExpr.Parameters[0];
var periodEndParam = baseExpr.Parameters[1];
var expr = baseExpr.Body
.ReplaceParameter(periodStartParam, periodStartValue)
.ReplaceParameter(periodEndParam, periodEndValue);
return Expression.Lambda<Func<T, bool>>(expr, entityParam);
}
请注意,您需要将传递的Expression<Func<T, DateTime>> 表达式的主体重新绑定(使用相同的ReplaceParameter 辅助方法)到要在结果表达式中使用的公共参数。
可以通过添加更多帮助方法来简化代码,例如Entity Framework + DayOfWeek,但同样,如果您打算大量使用它,更好的选择是使用一些现成的库,因为最后您将开始重新发明这些库的作用。