I have a DateRange class that I\'d like to apply to an IQueryable as a where predicate, automatically using the begin and end dates and automatically using an open or closed int
What you're looking to do here is to compose expressions; you're trying to apply one expression to the result of another. You can actually write a method to do that:
public static Expression> Compose(
this Expression> first,
Expression> second)
{
var param = Expression.Parameter(typeof(TSource));
var intermediateValue = first.Body.ReplaceParameter(first.Parameters[0], param);
var body = second.Body.ReplaceParameter(second.Parameters[0], intermediateValue);
return Expression.Lambda>(body, param);
}
It uses the following method to replace the parameter of an expression with an expression.
public static Expression ReplaceParameter(this Expression expression,
ParameterExpression toReplace,
Expression newExpression)
{
return new ParameterReplaceVisitor(toReplace, newExpression)
.Visit(expression);
}
public class ParameterReplaceVisitor : ExpressionVisitor
{
private ParameterExpression from;
private Expression to;
public ParameterReplaceVisitor(ParameterExpression from, Expression to)
{
this.from = from;
this.to = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node == from ? to : node;
}
}
This allows you to write your code as:
public IQueryable Apply(IQueryable source,
Expression> dateField)
{
var result = source;
if (BeginDate.HasValue)
{
if (BeginInclusive)
result = result.Where(dateField.Compose(date => date >= BeginDate));
else
result = result.Where(dateField.Compose(date => date > BeginDate));
}
if (EndDate.HasValue)
{
if (EndInclusive)
result = result.Where(dateField.Compose(date => date <= EndDate));
else
result = result.Where(dateField.Compose(date => date < EndDate));
}
return result;
}