score:2

Accepted answer

Try:

protected static IQueryable<TQueryResult> AddQueryFilter<TQueryResult, TParam>(
    IQueryable<TQueryResult> query, Expression<Func<TQueryResult, TParam, bool>> exp, TParam param)
{

    var rewriter = new ExpressionRewriter();
    rewriter.Subst(exp.Parameters[1], Expression.Constant(param, typeof(TParam)));
    var body = rewriter.Apply(exp.Body);
    var lambda = Expression.Lambda<Func<TQueryResult, bool>>(body, exp.Parameters[0]);
    return query.Where(lambda);
}

using ExpressionRewriter from this answer.

score:0

I hope somebody still can be searching for this topic, as me, in fact, so I'd like to suggest the following possibility.

Since .NET 4.0 has been released, you can use built-in expression tree visitors.

Here's an exapmple, which implements required functionality:

private class ExpressionConstantInjector<T, TConstant> : ExpressionVisitor
{
    private readonly TConstant toInject;
    private readonly ParameterExpression targetParam;

    public EntityExpressionListInjector(TConstant toInject)
    {
        this.toInject = toInject;
        targetParam = Expression.Parameter(typeof(T), "a");
    }

    public Expression<Func<T, bool>> Inject(Expression<Func<T, TConstant, bool>> source)
    {
        return (Expression<Func<T, bool>>) VisitLambda(source);
    }

    protected override Expression VisitLambda<T1>(Expression<T1> node)
    {
        return Expression.Lambda(Visit(node.Body), targetParam);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node.Type == typeof (TConstant))
            return Expression.Constant(toInject);
        return targetParam;
    }
}

Usage:

Expression<Func<Entity, List<int>, bool>> expression = (e, ids) => ids.Contains(e.Id);

var filterExpression 
    = new ExpressionConstantInjector<Entity, List<int>>(new List<int>{1, 2, 3})
    .Inject(expression); 
// filterExpression is like a => (1, 2, 3).Contains(a.Id) 
// filterExpression can be passed to EF IQueryables.

This solution is very local, not really reusable, but quiet simple (nah).

To be honest, [].Contains(id) is the only case I've tested. But I think it works.


Related Articles