Expression<Func<IQueryable<TIn>,TOut>>
,我想将其转换为Expression<Func<IEnumerable<TIn>,TOut>>
。实际上,我的最终目标是将给定的树编译成Func<IEnumerable<TIn>,TOut>>
,但是一旦完成转换,这就变得微不足道了。我可以将给定的lambda包装成一个在输入序列上首先调用AsQueryable()
的lambda,但是我认为这是非常低效的。我认为它必须遍历表达式树并在每次使用时进行编译。
有什么想法吗?
编辑:
当然,必须做出某些假设。转换只应知道如何将Queryable
中的静态方法的精确匹配转换为Enumerable
中的等效静态方法。否则,它要么失败,要么就做什么。我不在乎。
编辑2:
更多说明:
我要进行的过程的输入采用lambda表达式树。该lambda将IQueryable<T>
作为输入并产生一些输出。我想用等效逻辑生成一个新的lambda,但是它将IEnumerable<T>
作为输入并生成等效输出。
例如,所有对Queryable.Where(...)
的调用都应替换为对Enumerable.Where(...)
,Queryable.Select(...)
和Enumerable.Select(...)
等的调用
编辑3:
示例:
// 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.
我有一个Expression,TOut >>,我想将其转换为Expression
,TOut >>。实际上,我的最终目标是编译...
.AsQueryable()
做了几乎我想要的,我看了一下它在GitHub上的代码。它生成一个EnumerableQuery
类。查找执行查询后的类的功能,我进入了EnumerableRewriter.cs,它执行了我感兴趣的棘手的表达式树转换。
IQueryable<T>
参数而不是EnumerableQuery
常量。可能还剩下一些皱纹可以消除,但这是一个不错的开始。