Problème de requête compilé LINQ-to-SQL (fonctionne comme une requête non compilée)

c# compiled-query expression-trees linq linq-to-sql

Question

J'ai des méthodes d'extension C # sur IQueryable , par exemple FindNewCustomers() et FindCustomersRegisteredAfter(int year) et ainsi de suite, que j'utilise pour "chaîner" une requête ensemble pour LINQ to SQL.

Maintenant à mon problème: je veux créer des requêtes compilées, par exemple:

 private static Func<MyDataContext, SearchInfo, IQueryable<Customer>>
        CQFindAll = 
            CompiledQuery.Compile((MyDataContext dc, SearchInfo info) =>
                dc.Contacts.Select(c => c).FindCustomersRegisteredAfter(info.RegYear)
                           .OrderBy(info.OrderInfo)
                           .Skip(info.SkipCount)
                           .Take(info.PageSize));

La FindCustomersRegisteredAfter(int year) est une méthode d'extension prenant un IQueryable et le renvoyant. La méthode OrderBy est également une méthode d'extension (System.Linq.Dynamic) qui crée une expression dynamique basée sur une chaîne (par exemple, "FirstName ASC" va trier le champ FirstName par ordre croissant). Skip and Take sont les méthodes intégrées.

Ce qui précède (requête non compilée, mais requête régulière) fonctionne parfaitement . Une fois que je l'ai mis dans une requête compilée, j'ai frappé l'erreur suivante:

La méthode 'System.Linq.IQueryable`1 [Domain.Customer] FindCustomersRegisteredAfter [Client] (System.Linq.IQueryable`1 [Domain.Customer], Int32)' n'a pas de traduction prise en charge en SQL.

Encore une fois, cela fonctionne parfaitement si la requête est non compilée , juste une requête LINQ normale. L'erreur ne s'affiche qu'une fois à l'intérieur de CompiledQuery.Compile ().

Aidez-moi??!

Edit: Si je crée la requête via var query = (...) de la même manière qu'à l'intérieur de CompiledQuery.Compile, il s'agit du code SQL généré:

 private static Func<MyDataContext, SearchInfo, IQueryable<Customer>>
        CQFindAll = 
            CompiledQuery.Compile((MyDataContext dc, SearchInfo info) =>
                dc.Contacts.Select(c => c).FindCustomersRegisteredAfter(info.RegYear)
                           .OrderBy(info.OrderInfo)
                           .Skip(info.SkipCount)
                           .Take(info.PageSize));

Vous voyez donc que le code SQL est parfaitement traduisible, de sorte qu'il ne me reste plus qu'à saisir @ p0, @ p1 et @ p2 pour que cela fonctionne à plusieurs reprises! Quel est le problème avec CompiledQuery.Compile?!?

Mise à jour: Je comprends que OrderBy ne peut pas fonctionner (car ce n'est pas un paramètre @p). J'essaie toujours de comprendre pourquoi CompiledQuery.Compile ne fonctionnera pas avec mes méthodes d'extension. L'information sur Internet à ce sujet est pratiquement inexistante.

Réponse acceptée

Je crois que la requête compilée doit pouvoir être traduite en SQL, ce que votre méthode d'extension ne peut pas être. Si vous profilez le code SQL créé par votre requête "classique", vous constaterez peut-être qu'il sélectionne l'intégralité de la table pour qu'il puisse alimenter toutes les lignes dans votre méthode d'extension.

Vous feriez mieux de placer votre logique de filtrage dans la requête (dans l'arborescence des expressions) afin qu'elle puisse être traduite en SQL et exécutée côté serveur.

Le OrderBy est également un problème en raison du Skip. Vous devez rendre cela traduisible en SQL ou LINQ devra renvoyer toutes les lignes afin de les filtrer du côté client.

Si vous ne pouvez pas les exprimer sous forme d'expressions LINQ, envisagez de créer des fonctions SQL sur le serveur et de les mapper sur votre DataContext. LINQ pourra traduire ces appels en appels de fonction T-SQL.

MODIFIER:

Je suppose que je supposais que vos méthodes d'extension ne construisaient pas d'arbres d'expression. Pardon.

Considérez ce lien qui semble similaire à votre problème. Il fait référence à un autre lien qui va plus en détail.

Il semble que le problème concerne MethodCallExpression.

Le code est un peu long à publier ici, mais semblable au expandeur de tomasp.net, je visite toutes les expressions de l’arbre d’expression et si le nœud est un MethodCallExpression qui appelle une méthode renvoyant un arbre d’expression, je remplace cette arbre d'expression retourné en appelant la méthode.

Donc, il semble que le problème est que lors de la compilation de la requête, la méthode n'est pas exécutée, il n'y a donc pas d'arbre d'expression à traduire en SQL.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi