Paginare și Entity Framework

0

Problema

În aplicația mobilă, am încercat pentru a prelua date dintr-un tabel al meu bază de date SQL Server. Eu sunt, folosind EF și încerc să folosesc paginare pentru o performanță mai bună. Am nevoie pentru a prelua date de la ultimul element din tabel. deci, dacă tabelul are 20 de rânduri, am nevoie, pentru pagina 0, Id-uri de 20, 19, 18, 17, 16, apoi de pagina 1, Id-uri de 15, 14, 13, 12, 11 și așa mai departe...

Problema este următoarea: ce se întâmplă dacă, în timp ce utilizatorul "A" este descărcarea datelor din tabel, utilizatorul "B" adăugați rând? Dacă utilizatorul "A" obține Pagina 0 (deci Id-uri 20, 19, 18, 17, 16), și utilizatorul "B" în același moment se adaugă rând (așa ID 21), cu clasice de interogare, utilizatorul "a" de la pagina 1 va primi Id-uri 16, 15, 14, 13, 12... deci, un alt timp de IDENTITATE 16

Codul meu este foarte simplu:

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

Desigur Page este int care vin din partea de frontend.

Cum pot rezolva problema?

Am găsit o soluție, dar nu știu dacă e unul perfect. Am putea folosi

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

în loc

.Skip (RecordsForPagination * Page)

și, desigur, LastID întotdeauna este trimis de frontend.

Crezi că performanța este întotdeauna bun cu acest cod? Există o soluție mai bună?

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

Cel mai bun răspuns

1

Performanța impactul va depinde foarte mult de SQL Indicele de implementare și clauza order by. Dar sa nu atât de mult o întrebare despre performanta ca este vorba despre obtinerea rezultatelor scontate.

Stack Overflow este un exemplu foarte bun în cazul în care există un volum de activitate, astfel încât atunci când ajunge la sfârșitul de orice pagină, următoarea pagină poate conține înregistrări de la pagina pe care tocmai ați vizualizat deoarece de fond al sistemului recordset-a schimbat (mai multe posturi au fost adăugate)

Spun asta pentru că într-un sistem viu este general acceptat și, în unele cazuri, o temperatura de comportament. Ca dezvoltatorii apreciem adaugă cheltuielile generale de încercarea de a menține un singur set de rezultate și de a recunoaște că există, de obicei, o valoare mult mai mică în încercarea de a preveni ceea ce se pare ca suprapunerile ca voi repeta pagini.

Este de multe ori suficient pentru a explica de utilizatori, de ce se întâmplă acest lucru, în multe cazuri, ei vor accepta

Dacă e important pentru tine să-și mențină locul în original set de rezultate, atunci ar trebui să constrângă interogare cu un Where clauza, dar veți avea nevoie pentru a prelua fie Id-ul sau marca de timp în original interogare. În cazul în care vă sunt încercarea de a utiliza LastIDdar pentru a obține ultimul ID-ul ar avea nevoie de o interogare separată pe sine, deoarece clauză orderby va afecta.

Nu te poti folosi de .SkipWhile(x => x.ID >= LastID) pentru acest lucru, deoarece skip este un proces secvențial care este afectat de ordine și este dis-angajat prima instanță că expresia se evaluează la false, astfel încât în cazul în care comanda nu este bazat pe Idta-skip în timp ce ar putea duce și peste nici o înregistrare.

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();

În general, este mai simplu de a filtra de un punct în timp, deoarece acest lucru este cunoscut de la client, fara un drum la DB, dar în funcție de punerea în aplicare filtrarea pe datele pot fi mai puțin eficiente.

2021-11-22 23:55:04

Intenția mea nu a fost de a prelua LastID din DB (pentru că, așa cum ați spus, acest lucru ar putea fi afectate de Db în sine). intenția mea a fost aceasta: - Frontend prelua Pagina 0 și rezultatul sunt id-Urile 20, 19, 18, 17, 16 - Frontend prelua Pagina 1 și se trece la backend două param: Pagina 1 și LastID din Pagina anterioară (în acest caz LastID = 16) -> deci rezultatul va fi id-Urile 15, 14, 13, 12, 11... și așa mai departe. engleza mea nu e perfecta... ne spune același lucru?
user1106897

@user1106897 tehnica Chris se referă la este numit Keyset Paginare, și este, în general, mult mai bine decât de paginare de rânduri, care este despre ce vorbesti
Charlieface

Foarte mult este , de asemenea, o întrebare de performanță: paginare de rânduri este extrem de ineficient ca toate celelalte rânduri trebuie să fie citit de fiecare dată. Întrucât paginare de cheie (sau de timp în acest caz) este foarte eficient dacă nu există un sprijin index
Charlieface

Charlie fata va multumesc foarte mult pentru link-ul, foarte apreciat. Voi încerca să utilizați sfaturi
user1106897

Multumesc @Charlieface performanța remarca a fost mai mult ar trebui să fie "uita de performanță, SkipWhile(prin id) nu va rezolva problema, dacă vă schimbați pentru" Marea link-ul de asemenea!
Chris Schaller

În alte limbi

Această pagină este în alte limbi

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