Impaginazione e Entity Framework

0

Domanda

Nella mia app mobile, cerco di recuperare i dati da una tabella del mio database di SQL Server. Sto usando EF e cerco di usare la paginazione per migliorare le prestazioni. Ho bisogno di recuperare i dati dall'ultimo elemento della tabella. quindi se la tabella ha 20 righe, ho bisogno, per pagina 0, Id 20, 19, 18, 17, 16, quindi per pagina 1 Id 15, 14, 13, 12, 11 e così via...

Il problema è questo: cosa succede se, mentre l'utente "A" sta scaricando i dati dalla tabella, l'utente "B" aggiungi riga? Se l'utente "A" get Pagina 0 (quindi Id 20, 19, 18, 17, 16), e l'utente "B" nello stesso momento, aggiungi riga (quindi ID 21), con il classico query, l'utente "A" pagina 1 otterrà Id 16, 15, 14, 13, 12... così un'altra volta ID 16

Il mio codice è molto semplice:

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

Naturalmente Page è di tipo int che provengono dal frontend.

Come posso risolvere il problema?

Ho trovato una soluzione ma non so se è il perfetto. Potrei usare

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

invece

.Skip (RecordsForPagination * Page)

e, naturalmente, LastID sempre viene inviato dal frontend.

Pensi che le prestazioni sempre bene con questo codice? C'è una soluzione migliore?

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

Migliore risposta

1

L'impatto sulle prestazioni dipende molto SQL Indice di attuazione e la clausola order by. Ma non tanto per una questione di prestazioni in quanto si tratta di ottenere i risultati attesi.

Stack Overflow è un grande esempio dove c'è un volume di attività, in modo che quando si arriva alla fine di ogni pagina, la pagina successiva può contenere record della pagina che hai appena visto, perché il recordset sottostante è cambiato (più post sono stati aggiunti)

Ho fatto questo esempio perché in un sistema live è generalmente accettato e in alcuni casi non previsti comportamento. Come sviluppatori apprezziamo l'aggiunta di spese di cercare di mantenere un unico set di risultati, e di riconoscere che c'è di solito molto più basso valore nel cercare di evitare quello che sembra duplicazioni come è possibile scorrere le pagine.

Spesso è sufficiente per spiegare agli utenti perché in questo caso, in molti casi, sono accetti

Se è importante per voi per mantenere il posto in originale set di risultati, allora si dovrebbe limitare la query con un Where la clausola, ma è necessario recuperare l'Id o il timestamp della query originale. Nel tuo caso si sta tentando di utilizzare LastIDma per ottenere l'ultimo ID richiederebbe una query separata, proprio perché orderby clausola si influenzano.

Non si può davvero utilizzare .SkipWhile(x => x.ID >= LastID) per questo, perché skip è un processo sequenziale che è condizionato dall'ordine ed è dis-impegnati in prima istanza che l'espressione restituisce falsecosì se il vostro ordine non è basata su Id, salto, mentre potrebbe risultato è saltare nessun record.

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

È in genere più semplice filtro da un punto nel tempo in quanto è noto che il client senza un giro per il DB, ma a seconda dell'implementazione del filtro di date può essere meno efficiente.

2021-11-22 23:55:04

La mia intenzione era di non recuperare il LastID dal DB (perché, come hai detto tu, questo sarebbe influenzato dal Db stesso). la mia intenzione era questa: - Frontend recuperare Pagina 0 e il risultato sono gli Id 20, 19, 18, 17, 16 - Frontend recuperare Pagina 1 e passare al backend due param: Pagina 1 e LastID della Pagina precedente (in questo caso LastID = 16) -> quindi risultato sarà IDs 15, 14, 13, 12, 11... e così via. il mio inglese non è perfetto... stiamo dicendo la stessa cosa?
user1106897

@user1106897 La tecnica Chris si è chiamato Keyset Impaginazione, ed è generalmente molto meglio di paging da righe, che cosa si sta parlando
Charlieface

Molto è anche una questione di prestazioni: da file di paging è altamente inefficiente, in quanto tutte le righe precedenti devono essere lette in ogni momento. Considerando che il paging da chiave (o il tempo in questo caso) è molto efficiente, se c'è un supporto di indice
Charlieface

Charlie viso grazie mille per il link, molto apprezzato. Cercherò di utilizzare i consigli
user1106897

Grazie @Charlieface le prestazioni commento era più dovrebbe essere "dimenticate le prestazioni, SkipWhile(id) non risolve il problema se si cambia l'ordine" Grande legame troppo!
Chris Schaller

In altre lingue

Questa pagina è in altre lingue

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