I have an Expression<Func<IQueryable<TIn>,TOut>>, and I'd like to transform it into Expression<Func<IEnumerable<TIn>,TOut>>. Actually, my end goal is to compile the given tree into Func<IEnumerable<TIn>,TOut>>, but that is trivial once the transformation is done.
I could just wrap the given lambda into one that first calls AsQueryable() on the input sequence, but I assume this is highly inefficient. I assume it would have to traverse the expression tree and compile it every time it's used.
Any ideas?
EDIT:
Of course, certain assumptions have to be made. The transformation should only know how to transform exact matches of static methods from Queryable to equivalent static methods from Enumerable. Otherwise it should either fail or even just do whatever. I don't really care.
EDIT 2:
Some more clarifications:
The input to the process I want to make takes a lambda expression tree. That lambda takes an IQueryable<T> as input and produces some output. I want to produce a new lambda with equivalent logic, but that takes an IEnumerable<T> as input and produces the equivalent output.
For instance, all calls to Queryable.Where(...) should be replaced by calls to Enumerable.Where(...), Queryable.Select(...) with Enumerable.Select(...) etc.
EDIT 3:
An example:
// The expression I get transforms IQueryables, for instance this one:
Expression<Func<IQueryable<int>, double>> input =
qi => (double)qi.Sum() / qi.Count();
// I want an expression that transforms IEnumerables:
Expression<Func<IEnumerable<int>, double>> desiredOutput =
ei => (double)ei.Sum() / ei.Count();
// I can make it work like this:
var dirtyWorkaround = MakeDirtyWorkaround(input);
Expression<Func<IEnumerable<TIn>, TOut>> MakeDirtyWorkaround<TIn, TOut>(
Expression<Func<IQueryable<TIn>, TOut>> original)
{
// Doing this:
// ei => original.Invoke(ei.AsQueryable())
var asQueryableMethod = new Func<IEnumerable<TIn>, IQueryable<TIn>>(Queryable.AsQueryable).Method;
var parameter = Expression.Parameter(typeof(IEnumerable<TIn>), "ie");
return Expression.Lambda<Func<IEnumerable<TIn>, TOut>>(
Expression.Invoke(original,
Expression.Call(asQueryableMethod, parameter)),
parameter);
}
// But it's inefficient. Demonstration:
// The compiled expression can be cached.
var compiledDesired = desiredOutput.Compile();
var compiledDirty = dirtyWorkaround.Compile();
var exampleEnumerable = Enumerable.Range(1, 10);
var repetitions = 10_000;
// Desired test:
var desiredSw = Stopwatch.StartNew();
for (var i = 0; i < repetitions; ++i)
{
var exampleOutput = compiledDesired.Invoke(exampleEnumerable);
}
desiredSw.Stop();
// Dirty test:
var dirtySw = Stopwatch.StartNew();
for (var i = 0; i < repetitions; ++i)
{
// For every loop iteration, a query gets built on top of exampleEnumerable,
// then gets adapted to IEnumerable and compiled.
// It's ~1000 times slower in this case.
var exampleOutput = compiledDirty.Invoke(exampleEnumerable);
}
dirtySw.Stop();
Console.WriteLine($"Executed in {dirtySw.ElapsedMilliseconds} ms instead of {desiredSw.ElapsedMilliseconds} ms.");
// Executed in 3000 ms instead of 3 ms.
Given that
.AsQueryable()does almost what I want, I took a look at its code on GitHub. It generates anEnumerableQueryclass. Looking up what the class does once the query is executed, I got to EnumerableRewriter.cs, which does the tricky expression tree transformation I'm interested in.I hacked the class to transform
IQueryable<T>parameters instead ofEnumerableQueryconstants. There's probably wrinkles left to smooth out, but it's a great start.Quick test: