在我的一个项目中,我有一个ExpressionVisitor将提供的表达式转换为某个查询字符串。但在翻译之前,我需要将表达式中的所有参考值都评估为实际值。为此,我使用EntityFramework Project中的Evaluator.PartialEval方法。
假设我有这个问题:
var page = 100;
var query = myService.AsQueryable<Product>()
//.Where(x=>x.ProductId.StartsWith(p.ProductId))
.Skip(page)
.Take(page);
var evaluatedQueryExpr = Evaluator.PartialEval(query.Expression);
正如您所看到的,我已经评论了Where方法。在这种情况下,evaluateQueryExpr将不包含Take和Skip方法。
但是,如果我在Take或Skip之前使用Expression的任何其他方法,则Evaluator会正确评估表达式并将其完全返回。
我发现问题发生在Evaluator类的第80行:
return Expression.Constant(fn.DynamicInvoke(null), e.Type);
你能解释一下为什么会发生这种情况并提出一个解决方法吗?
更新 这是a project on github
LinqToSolrQueriable inherited from IOrderedQueryable LinqToSolrProvider inherited from IQueryProvider包括导致问题的行范围
答案 0 :(得分:2)
好消息是表达式并没有真正减少(Skip
和Take
仍然存在:),但只是从MethodCallExpression
转换为ConstantExpression
包含原始表达式:
query.Expression :
.Call System.Linq.Queryable.Take(
.Call System.Linq.Queryable.Skip(
.Constant<LinqToSolr.Query.LinqToSolrQueriable`1[LinqToSolrTest.Product]>(LinqToSolr.Query.LinqToSolrQueriable`1[LinqToSolrTest.Product]),
100),
100)
evaluatedQueryExpr:
.Constant<System.Linq.IQueryable`1[LinqToSolrTest.Product]>(LinqToSolr.Query.LinqToSolrQueriable`1[LinqToSolrTest.Product])
此处调试显示给您一个错误的印象。如果您使用ConstaintExpression.Value
,则会看到IQueryable<Product>
Expression
属性与原始query.Expression
完全相同。
坏消息是,这不是你对PartialEval
的期望 - 事实上它在这种情况下没有做任何有用的事情(除了可能破坏你的查询翻译逻辑)。
那么为什么会这样呢?
您从EntityFramework.Extended库中使用的方法依次来自MSDN示例Walkthrough: Creating an IQueryable LINQ Provider(如评论中所示)。可以注意到PartialEval
方法有两个重载 - 一个用Func<Expression, bool> fnCanBeEvaluated
参数来识别给定表达式节点是否可以成为本地函数的一部分(换句话说) ,部分评估或未部分评估),以及没有这样的参数(由您使用),它只是调用第一个传递以下谓词:
private static bool CanBeEvaluatedLocally(Expression expression)
{
return expression.NodeType != ExpressionType.Parameter;
}
效果是它会停止评估ParameterExpression
类型表达式以及任何直接或间接包含的表达式ParameterExpression
。最后一个应该解释你正在观察的行为。当查询在Where
/ {{1}之前包含带有参数化lambda表达式(因此参数)的Skip
(以及基本上任何LINQ运算符)时}调用,它将停止评估包含的方法(您可以从上面的Take
调试视图中看到 - query.Expression
调用将在Where
内。
现在,MSDN示例使用此重载来评估具体嵌套 Skip
方法lambda表达式,并且通常不适用于任何类型的表达式,如Where
。事实上,链接的项目在QueryCache类中的单个位置使用IQueryable.Expression
方法,并且还调用另一个重载传递different predicate除了PartialEval
之外还停止了评估结果类型为ParameterExpressions
的任何表达式。
我认为这也是你问题的解决方案:
IQueryable