The inject parameter value for TDelegate in the expression <TDelegate> and the abbreviation
I need to reduce the expression
Expression<Func<TQueryResult, TParam, bool>>
to
Expression<Func<TQueryResult, bool>>
introducing TParam value as a constant in the expression.
Specific example:
protected IQueryable<TQueryResult> AddQueryFilter<TQueryResult, TParam>(IQueryable<TQueryResult> query, Expression<Func<TQueryResult, TParam, bool>> exp, TParam param) { object obj = param; if (obj is string) { if (!string.IsNullOrWhiteSpace((string) obj)) { var reducedExp = new Expression<Func<TQueryResult, bool>>() // ... // the magic that I need to inject param value //.. return query.Where(reducedExp); } } else if (obj is DateTime) { //... return query.Where(reducedExp); } else throw new InvalidOperationException("Param type not supported"); return query; } //usage var qr = Manager.Invoices.Query; qr = AddQueryFilter(qr, (invoice, value) => value == invoice.Number, numberEdit.Text); qr = AddQueryFilter(qr, (invoice, value) => value == invoice.Date, dateEdit.Date); qr = AddQueryFilter(qr, (invoice, value) => invoice.Description.Contains(value), descEdit.Text); +4
2 answers
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 .
+2
I hope that someone can still search for this topic, just like me, in fact, so I would like to offer the next opportunity.
Since .NET 4.0 is released, you can use the built-in declaration tree trees .
Here's an exapmple that implements the 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; } } Using:
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, but not very reusable, but simple (nah).
Honestly, [].Contains(id) is the only case I tested. But I think it works.
0