2010-01-05 7 views
1

J'utilise LINQ to SQL (en fait, il est dynamique LINQ to SQL qui vous permet de passer des chaînes à l'exécution des clauses où, orderby etc.) Mais je me certains résultats différents et il semble être basé sur si le T-SQL sous-jacent utilise le mot-clé TOP ou en utilisant BETWEEN.LINQ to SQL fournit des résultats différents lorsque vous utilisez TOP et entre

J'ai essayé de le décomposer le problème dans un petit exemple, voici le scénario:

J'utilise un modèle référentiel et la méthode suivante qui relie simplement 2 tables avec une jointure externe gauche.

public IQueryable<TestGalleryViewModel> FetchGalleryItems() 
    { 

     var galleryItems = from painting in Gallery 
          join artist in Artists 
           on painting.ArtistID equals artist.ArtistID 

          into paintingArtists 
          from artist in paintingArtists.DefaultIfEmpty() 

          select new TestGalleryViewModel 
          { 

           Id = painting.PaintingID, 
           ArtistName = artist == default(Artist) ? "" : artist.Surname + " " + artist.Forenames, 
          }; 

     return galleryItems; 
    } 

J'ai alors une petite méthode de test qui utilise la méthode FetchGalleryItems:

 var query = respository.Test_FetchGalleryItems().Where("ArtistName.Contains(\"Adams Charles James\")"); 

     var orderedlist = query.OrderBy("ArtistName asc"); 
     var page1 = orderedlist.Skip(0).Take(5); 
     var page2 = orderedlist.Skip(5).Take(5); 

Le orderedList contient les valeurs sous-jacentes suivantes:

176 ADAMS Charles James 
620 ADAMS Charles James 
621 ADAMS Charles James 
660 ADAMS Charles James 
683 ADAMS Charles James 
707 ADAMS Charles James 
735 ADAMS Charles James 
739 ADAMS Charles James 
740 ADAMS Charles James 
741 ADAMS Charles James 

Ce qui est ce que j'attendais. Mais page1 contient

707 ADAMS Charles James 
683 ADAMS Charles James 
660 ADAMS Charles James 
621 ADAMS Charles James 
620 ADAMS Charles James 

qui, comme vous pouvez le voir est PAS les 5 premiers articles. Page2 contient

707 ADAMS Charles James 
735 ADAMS Charles James 
739 ADAMS Charles James 
740 ADAMS Charles James 
741 ADAMS Charles James 

Whis est ce que j'attendais, il est des articles 6 à 10.

Le T-SQL sous-jacente pour page1 est

SELECT TOP (5) [t3].[PaintingID] AS [Id], [t3].[value] AS [ArtistName] 
FROM (
    SELECT [t0].[PaintingID], 
     (CASE 
      WHEN [t2].[test] IS NULL THEN CONVERT(NVarChar(101),'') 
      ELSE ([t2].[Surname] + ' ') + [t2].[Forenames] 
     END) AS [value] 
    FROM [dbo].[Gallery] AS [t0] 
    LEFT OUTER JOIN (
     SELECT 1 AS [test], [t1].[ArtistID], [t1].[Surname], [t1].[Forenames] 
     FROM [dbo].[Artists] AS [t1] 
     ) AS [t2] ON [t0].[ArtistID] = ([t2].[ArtistID]) 
    ) AS [t3] 
WHERE [t3].[value] LIKE '%Adams Charles James%' 
ORDER BY [t3].[value] 

avis qu'il utilise TOP (5)

Le T-SQL sous-jacente pour page2 est

SELECT [t4].[PaintingID] AS [Id], [t4].[value] AS [ArtistName] 
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t3].[value], [t3].[Surname], [t3].[Forenames]) AS [ROW_NUMBER], [t3].[PaintingID], [t3].[value] 
    FROM (
     SELECT [t0].[PaintingID], 
      (CASE 
       WHEN [t2].[test] IS NULL THEN CONVERT(NVarChar(101),'') 
       ELSE ([t2].[Surname] + ' ') + [t2].[Forenames] 
      END) AS [value], [t2].[Surname], [t2].[Forenames] 
     FROM [dbo].[Gallery] AS [t0] 
     LEFT OUTER JOIN (
      SELECT 1 AS [test], [t1].[ArtistID], [t1].[Surname], [t1].[Forenames] 
      FROM [dbo].[Artists] AS [t1] 
      ) AS [t2] ON [t0].[ArtistID] = ([t2].[ArtistID]) 
     ) AS [t3] 
    WHERE [t3].[value] LIKE '%Adams Charles James%' 
    ) AS [t4] 
WHERE [t4].[ROW_NUMBER] BETWEEN 5 + 1 AND 5 + 5 
ORDER BY [t4].[ROW_NUMBER] 

avis qu'il utilise ENTRE

Quand je coller les commandes T-SQL dans la gestion SQL Express Studio Je reçois les résultats que je viens de décrire. Si je le T-SQL page2 et modifié la ligne

WHERE [t4].[ROW_NUMBER] BETWEEN 5 + 1 AND 5 + 5 

être

WHERE [t4].[ROW_NUMBER] BETWEEN 1 AND 5 

-je obtenir les résultats que j'attendais pour page1. c'est-à-dire les 5 premiers articles.

176 ADAMS Charles James 
620 ADAMS Charles James 
621 ADAMS Charles James 
660 ADAMS Charles James 
683 ADAMS Charles James 

Donc, en un mot lorsque le T-SQL utilise entre au lieu de TOP-je obtenir les résultats que j'attendais.

J'utilise le filtrage (clause where), le tri (orderBy) et la pagination (sauter et prendre) sur mon application et la nécessité de gérer cela assez générique.

  • Y at-il un moyen de forcer à utiliser LINQ la ENTRE syntaxe au lieu de TOP?
  • Dois-je aborder ce différemment? pour le long post

Toutes mes excuses.

Cordialement, Simon

+0

Notez également que vous avez une clause Order By différente dans la deuxième requête SQL. Essayez d'évaluer var orderedlist = query.OrderBy ("ArtistName asc"). ToList(); d'abord. Et puis obtenir des pages à partir de là. –

Répondre

0

Quelle que soit la façon dont le SQL est généré (LINQ ou autre), si vous ORDER BY une colonne qui a des valeurs en double, vous pouvez obtenir des résultats différents à chaque fois que vous exécutez la requête.

Lorsque vous ORDER BY [t3].[value] vous triez sur une colonne contenant de nombreuses valeurs en double.

Vous pouvez tester en exécutant une très simple SQL SELECT de Management Studio. Chaque fois que vous l'exécutez, vous obtenez un résultat différent.

Une façon d'obtenir des résultats cohérents est d'utiliser ROW_NUMBER comme vous l'avez fait. Alternativement, l'ajout de toute autre colonne au ORDER BY unique entraînera toujours le retour des résultats dans le même ordre. Peu importe si cette autre colonne a quelque chose à voir avec votre requête, juste qu'elle est unique.

+0

Merci DOK, Vous dites une façon d'obtenir reults cohérente est d'utiliser « ROW_NUMBER comme vous l'avez fait ». En fait, je n'ai pas utilisé ROW_NUMBER, linq to Sql a généré ce code et je ne semble pas avoir de contrôle dessus. C'était vraiment ma question - puis-je dire à linq à sql d'utiliser "row_number" et "between" au lieu de simplement "TOP" pour la première page. En fin de compte, j'ai dû ajouter une colonne unique à toutes mes entités et virer à la fin de la commande, comme vous l'avez suggéré. C'était quelque chose que je recherchais déjà mais je voulais savoir s'il y avait une meilleure solution. Merci pour votre réponse. –