La Pagination et Entity Framework

0

La question

Dans mon application mobile, j'essaie de récupérer des données à partir d'une table de ma base de données SQL Server. Je suis à l'aide d'EF et j'essaie d'utiliser la pagination pour de meilleures performances. J'ai besoin de récupérer des données à partir du dernier élément de la table. donc, si la table dispose de 20 lignes, j'ai besoin, pour la page 0, Id 20, 19, 18, 17, 16, puis pour la page 1 Id 15, 14, 13, 12, 11 et ainsi de suite...

Le problème est: est-ce que si, alors que l'utilisateur "A" est en cours de téléchargement des données du tableau, l'utilisateur "B" ajouter une ligne? Si l'utilisateur "Une" de la Page 0 (donc les Id 20, 19, 18, 17, 16), et l'utilisateur "B" dans le même moment ajouter une ligne (donc ID 21), avec les classiques de la requête, l'utilisateur "A" pour la page 1 obtiendrez Id 16, 15, 14, 13, 12... donc, une autre fois, ID 16

Mon code est très simple:

int RecordsForPagination = 5; 
var list = _context.NameTable
                   .Where(my_condition)
                   .OrderByDescending(my_condition_for ordering)
                   .Skip (RecordsForPagination * Page)
                   .Take (RecordsForPagination)
                   .ToList();

Bien sûr Page est de type int qui viennent de l'interface.

Comment puis-je résoudre le problème?

J'ai trouvé une solution mais je ne sais pas si c'est le parfait. Je pourrais utiliser

.SkipWhile(x => x.ID >= LastID) 

au lieu de cela

.Skip (RecordsForPagination * Page)

et bien sûr LastID toujours est envoyé à partir du frontend.

Pensez-vous que la performance est toujours bon avec ce code? Est-il une meilleure solution?

entity-framework linq sql-server
2021-11-22 23:06:34
1

La meilleure réponse

1

L'impact sur les performances dépendra grandement de votre Index SQL de mise en œuvre et la clause order by. Mais ce n'est pas tellement une question à propos de la performance comme il s'agit d'obtenir les résultats escomptés.

Stack Overflow est un excellent exemple où il y a un volume d'activité tels que, lorsque vous arrivez à la fin de la page, la page suivante peut contenir des enregistrements à partir de la page que vous avez tout simplement parce que le sous-jacent jeu d'enregistrements a changé (plus de postes ont été ajoutés)

Je soulève cette question parce que dans un système, il est généralement accepté et, dans certains cas, un attendu de comportement. En tant que développeurs, nous apprécions l'ajout des frais généraux d'essayer de maintenir un seul ensemble de résultats et de reconnaître qu'il n'y est généralement plus faible que la valeur en essayant d'éviter ce qui ressemble à des duplications que vous parcourez les pages.

Il suffit souvent d'expliquer aux utilisateurs pourquoi cela se produit, dans de nombreux cas, ils vont l'accepter

Si c'était important pour vous de maintenir la place de l'original jeu de résultats, alors vous devriez limiter la requête avec un Where la clause, mais vous aurez besoin de récupérer l'Id ou le timestamp dans la requête d'origine. Dans votre cas, vous tentez d'utiliser LastIDmais pour récupérer le dernier ID nécessiterait une requête distincte sur son propre parce que la clause orderby sera l'impact.

Vous ne pouvez pas vraiment utiliser .SkipWhile(x => x.ID >= LastID) pour cela, parce que sauter est un processus séquentiel qui est affectée par l'ordre, et dis-engagés de la première instance que l'expression est évaluée à false, donc si votre commande n'est pas basé sur Idvotre sauter alors que pourrait entraîner l'est de sauter des pas de disques.

int RecordsForPagination = 5; 
int? MaxId = null;
...
var query = _context.NameTable.Where(my_condition);
// We need the Id to constraint the original search
if (!MaxId.HasValue)
    MaxId = query.Max(x => x.ID);

var list = query.Where(x => x.ID <= MaxId)
                .OrderByDescending(my_condition_for ordering)
                .Skip(RecordsForPagination * Page)
                .Take(RecordsForPagination);
                .ToList();

Il est généralement plus simple de filtre par un point dans le temps comme cela est connu du client sans un aller-retour à la DB, mais en fonction de la mise en œuvre du filtrage sur les dates peuvent être moins efficaces.

2021-11-22 23:55:04

Mon intention n'était pas de récupérer LastID de DB (parce que, comme vous l'avez dit, ce serait affecté par Db lui-même). mon intention était-ce: - Frontend récupérer la Page 0 et le résultat est Id 20, 19, 18, 17, 16 - Frontend récupérer la Page 1 et passer à backend deux param: Page 1 et LastID de la Page précédente (dans ce cas LastID = 16) -> donc le résultat sera Id 15, 14, 13, 12, 11... et ainsi de suite. mon anglais n'est pas parfait... sommes-nous dire la même chose?
user1106897

@user1106897 La technique de Chris, qui est qui est appelé le jeu de clés de la Pagination, et est généralement beaucoup mieux que la pagination par lignes, ce qui est ce que vous parlez
Charlieface

Il est également question de la performance: la recherche par des lignes est très inefficace que toutes les lignes doivent être lues à chaque fois. Alors que la pagination par clé (ou le temps dans ce cas) est très efficace s'il est un indice
Charlieface

Charlie visage merci beaucoup pour le lien, vraiment apprécié. Je vais essayer d'utiliser les conseils
user1106897

Merci @Charlieface la performance remarque était plus censé être "oubliez la performance, SkipWhile(id) ne va pas résoudre le problème si vous modifiez l'ordre de" la Grande lien aussi!
Chris Schaller

Dans d'autres langues

Cette page est dans d'autres langues

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................