Les bonnes nouvelles sont que l'expression est pas vraiment réduite (Skip
et Take
sont toujours là :), mais est simplement converti de MethodCallExpression
à ConstantExpression
contenant l'expression originale:
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])
Ici, l'affichage de débogage vous donne une fausse impression. Si vous prenez le ConstaintExpression.Value
, vous verrez que c'est une propriété IQueryable<Product>
avec Expression
étant exactement la même que l'original query.Expression
.
Les mauvaises nouvelles sont que ce n'est pas ce que vous attendez de PartialEval
- en fait, il ne fait rien d'utile dans ce cas (sauf potentiellement casser votre logique de traduction de requête).
Alors, pourquoi cela se produit-il?
La méthode que vous utilisez de la bibliothèque EntityFramework.Extended est à son tour pris (comme il est indiqué dans les commentaires) de MSDN Sample Walkthrough: Creating an IQueryable LINQ Provider.On peut remarquer que la méthode PartialEval
a deux surcharges - une avec Func<Expression, bool> fnCanBeEvaluated
paramètre utilisé pour identifier si un noeud d'expression donné peut faire partie de la fonction locale (en d'autres termes, pour être partiellement évalué ou non), et un sans tel paramètre (utilisé par vous) qui appelle simplement le premier passage de l'attribut suivant:
private static bool CanBeEvaluatedLocally(Expression expression)
{
return expression.NodeType != ExpressionType.Parameter;
}
l'effet est qu'il arrête l'évaluation des expressions de type ParameterExpression
et des expressions contenant des directement ou indirectement ParameterExpression
. Le dernier devrait expliquer le comportement que vous observez. Lorsque la requête contient Where
(et essentiellement tout opérateur LINQ) avec l'expression paramétrisé lambda (d'où paramètre) avant que les appels Skip
/Take
, il arrêterait l'évaluation des méthodes contenant (que vous pouvez voir dans la vue de débogage query.Expression
ci-dessus - l'appel Where
sera à l'intérieur du Skip
).
Maintenant, cette surcharge est utilisé par l'exemple MSDN pour évaluer une méthode imbriquéeWhere
béton expression lambda et est généralement pas applicable pour tout type d'expression comme IQueryable.Expression
. En fait, le projet lié utilise la méthode PartialEval
en un seul endroit à l'intérieur de la classe QueryCache, et appelle également l'autre surcharge en passant un different predicate qui, en plus de ParameterExpressions
, arrête l'évaluation de toute expression avec le type de résultat IQueryable
.
qui je pense est la solution de votre problème ainsi:
var evaluatedQueryExpr = Evaluator.PartialEval(query.Expression,
// can't evaluate parameters or queries
e => e.NodeType != ExpressionType.Parameter &&
!typeof(IQueryable).IsAssignableFrom(e.Type)
);
Je ne vois pas ... 'var = evaluatedQueryExpr Evaluator.PartialEval (nouveau [] {{} nouveau produit, nouveau produit {}} .AsQueryable() .Skip (page) .Take (page) .Expression) 'sur autre' IQueryable <> '... Cela pourrait être un" effet spécial "de EF' IQueryable <> '. –
xanatos
Mais notez que pour le 'Skip (page)' vous n'entrerez pas dans la ligne 80: le 'Skip (page)' est déjà 'Skip (100)' dans la 'query'. La variable 'page' est" read "quand la variable' query' est initialisée. Les lignes 74-76 sont donc exécutées: 'if (e.NodeType == ExpressionType.Constant) {return e;' (* 100 * est une constante). – xanatos
c'est ce que je ne peux pas comprendre. Je débogue la méthode Evaluate. Je vois que je passe une expression à cette méthode '{value (Linq.SolrQueryable'1 [Product]). Skip (100) .Take (100)}'. Mais à l'intérieur dans la ligne 80 'Expression.Constant (fn.DynamicInvoke (null), e.Type))' il est déjà converti en '{value (SolrQueryable'1 [Product])}'. Cependant, votre exemple pur fonctionne correctement :(donc pour une raison quelconque 'fn.DynamicInvoke' lâche prendre et sauter quand aucun 'where' fourni auparavant – DolceVita