2009-08-31 8 views
3

Vous venez de passer de Subsonic 2.2 ActiveRecord à 3.0.0.3. Je suis en train d'utiliser LINQ pour faire une paginée Trouver requête comme ceci (mon objet/table est appelée « dépôt »):Subsonic 3.0.0.3 Paging SQL en utilisant Linq

Repository.Find(item => item.DocumentTitle.Contains(searchTerm)) 
    .OrderBy(i => i.DocumentTitle).Skip((currentPage - 1) * itemsPerPage) 
    .Take(itemsPerPage); 

Quand je considère le SQL généré par cette requête en utilisant SQL Server Profiler, il n'y a paging dans le SQL, toute la pagination se fait en mémoire en C#. Maintenant, le langage de requête Subsonic a une belle procédure GetPaged qui fonctionne bien, mais je pensais que LINQ était supposé faire cela aussi. Ai-je manqué quelque chose ici ou est-ce une limitation de LINQ?

Je suis conscient de la fonction Repository.GetPaged(), mais cela n'a pas assez de paramètres - je dois faire un tri dynamique, ainsi qu'un Find().

Répondre

5

En faisant d'autres essais, cette déclaration fonctionne correctement:

(from i in dataContext.Repositories 
where i.DocumentTitle.Contains(searchTerm) 
orderby i.DateCreated ascending select i) 
.Skip((currentPage - 1) * itemsPerPage).Take(itemsPerPage); 

Lorsqu'il est exécuté, l'instruction LINQ ci-dessus REVIENT correctement repaginés sql.

La seule conclusion que je peux arriver est que lorsque vous utilisez la méthode syntaxe enchaînant, une fois que vous êtes en dehors de l'expression lamda initiale

Repository.Find(item => item.DocumentTitle.Contains(searchTerm)) 

l'interpréteur subsonique SQL arrête la création de SQL pour toutes les méthodes enchaînées sur la fin

.OrderBy(i => i.DocumentTitle).Skip(15).Take(10); 

Ou, est-ce que je fais totalement quelque chose de mal ici? Quelqu'un a un aperçu?

+0

A fait encore plus de recherches. Apparemment, cela est dû à la façon dont C# compile une expression linq. Le compilateur doit savoir au moment du design ce que l'instruction va être. Si ce n'est pas le cas, l'instruction ne peut pas être reflétée par le "ExpressionVisitor" de SubSonic (ce n'est pas propre à SubSonic) car il ne s'agit plus d'une seule "Expression". – Steve

+0

Il existe des bibliothèques tierces qui vous permettent de construire votre instruction linq en plusieurs étapes, mais cela ajoute plus de dépendances externes à votre projet. SubSonic semble déjà avoir certaines de ces fonctions, mais je n'en sais pas assez pour le moment, alors suggérez un moyen de les utiliser pour y parvenir. – Steve

1

Vous pouvez trier GetPaged en ajoutant « desc » au champ de tri, mais ...

Paging devrait fonctionner - je regarde le SQL d'appel devant moi et il ne se fait pas dans la mémoire. Comment testez-vous cela? Si vous utilisez "ToList()" qui va exécuter la requête - jetez un oeil sur le profileur alors.

+0

La fonction GetPaged serait idéale, sauf que j'en ai besoin pour exécuter un Find() aussi, donc FindGetPaged() si vous voulez. c'est le SQL que je vois venir par le biais de la requête que j'ai posté (j'ai essayé aussi ToList (), j'avais passer le IEnumerable à une source de données ListView). exec sp_executesql N'SELECT [t0] [CategoryName] , [t0]. [DateCreated], [t0]. [DocumentFileType], [t0]. [TitreTitre], [t0]. [FileContent], [t0]. [RepositoryId] FROM [dbo]. [mag_Repository] AS t0 OERE ([t0]. [TitreTitre] COMME ''% '' + @ p0 + ''% '') ', N' @ p0 nvarchar (4000) ', @ p0 = N' ' – Steve

+0

Pour le tester attaché le débogueur VS, a démarré le profileur uniquement pour ma base de données et puis rompre à cette fonction: Liste retList = Repository .Find (item => item.DocumentTitle.Contains (searchTerm)) .OrderByDescending (i => i.DocumentTitle) .Skip ((currentPage - 1) * itemsPerPage) .Take (itemsPerPage) .ToListe (); et regarder l'instruction sql passer par le profil lorsque je tape F10. – Steve

+0

J'ai aussi fait un test de GetPaged juste pour m'assurer que je voyais bien le code SQL, et cela s'est bien passé avec l'instruction RowNumber() paging sql. Je suis donc incapable de comprendre ce que je fais de mal ici. – Steve

0

Un peu en retard, mais ...

Repository.Find() 

retours IList si la requête est exécutée, par conséquent, SQL exécution sans pagination puis

.Skip(x).Take(x) 

est fait en mémoire. Essayez

Repository.All().Where(expression).Skip(x).Take(x) 

qui tous et non retourner IQueryable dont énumérer les objets et ainsi la pagination se fait dans SQL en utilisant la fonction ROW_NUMBER().

Cela dit Subsonic 3 simple référentiel génère le SQL

exec sp_executesql N'SELECT [t0].[Id], [t0].[IsDeleted], [t0].[Name], [t0].[ParentUuid], [t0].[Uuid] 
FROM (SELECT [t1].[Id], [t1].[IsDeleted], [t1].[Name], [t1].[ParentUuid], ROW_NUMBER() OVER() AS rownum, [t1].[Uuid] 
FROM [Sites] AS t1 
WHERE (([t1].[ParentUuid] = @p0) AND ([t1].[IsDeleted] = 0))) AS t0 
WHERE [t0].[rownum] BETWEEN (20 + 1) AND (20 + 10)',N'@p0 uniqueidentifier',@p0='00000000-0000-0000-0000-000000000000' 

suivant qui jette une exception

Unhandled Exception: System.Data.SqlClient.SqlException: The ranking function "ROW_NUMBER" must have an ORDER BY clause. 

il semblerait qu'il y ait un bogue dans Subsonic :-(

+0

Je ne dirais pas que c'est un bug SubSonic parce que SubSonic traduit votre requête en plain sql. Comme les exceptions indiquent que ROW_NUMBER doit avoir une clause ORDER BY, vous devez également ajouter un mot-clé orderby à votre requête linq, de sorte que subsonic génère également un ordre par (btw mysql ne lève pas d'exception dans ce cas car LIMIT (x , y) ne nécessite pas de ORDER BY –

+0

Peut-être pas un bug, d'accord J'ai mis à jour une version locale de subsonic pour tester si une clause order by a été ajoutée à toute requête contenant la fonction ROW_NUMBER, s'il n'y a pas de commande par J'insère ORDER BY Id dans la partie OVER de ROW_NUMBER pour éviter l'exception, ce n'est pas idéal et c'est spécifique à l'environnement (TOUTES mes classes DOM DOIVENT hériter de DomBase et auront une propriété ID). –