Reputation: 2343
Is there any way to apply a manually created expression tree from one IQueryable to another? For example:
IQueryable<string> p = Enumerable.Empty<string>().AsQueryable();
p = p.Where(pp => pp[0] == 'A');
p = p.Skip(2).Take(4);
p = p.OrderBy(pp => pp.Length);
var vv = new[] {"Afss", "Acv", "Adfv", "Bcvx", "Ng"}.AsQueryable();
// Filtering of the vv collection with expression from p
var filteredResult = vv.Filter(p.Expression);
Or we have to separately apply the Where
and OrderBy
expressions?
Upvotes: 6
Views: 4853
Reputation: 111
Simple CreateQuery
call does not work, because provider uses data source of the passed expression.
Before executing expression you need to replace Enumerable.Empty<string>()
by a collection with your data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
IQueryable<string> p = Enumerable.Empty<string>().AsQueryable();
p = p.Where(pp => pp[0] == 'A');
p = p.Skip(2).Take(4);
p = p.OrderBy(pp => pp.Length);
var vv = new[] {"Afss", "Acv", "Adfv", "Bcvx", "Ng"}.AsQueryable();
// Filtering of the vv collection with expression from p
var expr = ExpressionTreeConstantReplacer.CopyAndReplace(p.Expression, typeof(EnumerableQuery<string>), vv);
var filteredResult = vv.Provider.CreateQuery<string>(expr);
Console.Write("Source: \t");
foreach(var temp in vv)
Console.Write("{0} ", temp);
Console.WriteLine();
Console.Write("Filtered: ");
foreach(var temp in filteredResult)
Console.WriteLine(temp);
}
class ExpressionTreeConstantReplacer<T> : ExpressionVisitor
{
Type originalType;
T replacementConstant;
internal ExpressionTreeConstantReplacer(Type originalType, T replacementConstant)
{
this.originalType = originalType;
this.replacementConstant = replacementConstant;
}
protected override Expression VisitConstant(ConstantExpression c)
{
return c.Type == originalType ? Expression.Constant(replacementConstant) : c;
}
}
class ExpressionTreeConstantReplacer
{
internal static Expression CopyAndReplace<T>(Expression expression, Type originalType, T replacementConstant)
{
var modifier = new ExpressionTreeConstantReplacer<T>(originalType, replacementConstant);
return modifier.Visit(expression);
}
}
}
Upvotes: 6
Reputation: 203847
You can access the provider of the query and use CreateQuery
to create a query based on an expression, and you can get the expression from the other query to pass to it:
var filteredResult = vv.Provider.CreateQuery<string>(p.Expression);
Upvotes: 3
Reputation: 1770
I had a similar case in which I had to send filter/sort expressions over WCF. I could not find a way to use a single instance for both the filter and the sort expression. I ended up using SerializableExpression from InterLinq. Doesn't solve your particular question but I hope it helps.
Upvotes: 0