Impaginazione
L'impaginazione si riferisce al recupero dei risultati nelle pagine, anziché contemporaneamente; questa operazione viene in genere eseguita per set di risultati di grandi dimensioni, in cui viene visualizzata un'interfaccia utente che consente all'utente di passare alla pagina successiva o precedente dei risultati.
Avviso
Indipendentemente dal metodo di impaginazione usato, assicurarsi sempre che l'ordine sia completamente univoco. Ad esempio, se i risultati vengono ordinati solo per data, ma possono essere presenti più risultati con la stessa data, i risultati potrebbero essere ignorati durante l'impaginazione mentre vengono ordinati in modo diverso tra due query di impaginazione. L'ordinamento per data e ID (o qualsiasi altra proprietà o combinazione di proprietà univoche) rende l'ordinamento completamente univoco ed evita questo problema. Si noti che i database relazionali non applicano alcun ordinamento per impostazione predefinita, anche nella chiave primaria.
Impaginazione offset
Un modo comune per implementare la paginazione con i database consiste nell'usare Skip
e Take
(OFFSET
e LIMIT
in SQL). Data una dimensione di pagina di 10 risultati, la terza pagina può essere recuperata con EF Core come indicato di seguito:
var position = 20;
var nextPage = context.Posts
.OrderBy(b => b.PostId)
.Skip(position)
.Take(10)
.ToList();
Purtroppo, sebbene questa tecnica sia molto intuitiva, presenta anche alcune gravi carenze:
- Il database deve comunque elaborare le prime 20 voci, anche se non vengono restituite all'applicazione; In questo modo viene creato un carico di calcolo potenzialmente significativo che aumenta con il numero di righe ignorate.
- Se si verificano aggiornamenti simultaneamente, l'impaginazione potrebbe finire per ignorare determinate voci o visualizzarle due volte. Ad esempio, se una voce viene rimossa man mano che l'utente passa dalla pagina 2 alla 3, l'intero set di risultati "sposta verso l'alto" e una voce viene ignorata.
Impaginazione keyset
L'alternativa consigliata all'impaginazione basata su offset, talvolta denominata impaginazione keyset o impaginazione basata su seek, consiste nell'usare semplicemente una WHERE
clausola per ignorare le righe, anziché un offset. Ciò significa ricordare i valori rilevanti dell'ultima voce recuperata (anziché il relativo offset) e chiedere le righe successive dopo tale riga. Si supponga, ad esempio, che l'ultima voce dell'ultima pagina recuperata abbia un valore ID pari a 55, è sufficiente eseguire le operazioni seguenti:
var lastId = 55;
var nextPage = context.Posts
.OrderBy(b => b.PostId)
.Where(b => b.PostId > lastId)
.Take(10)
.ToList();
Supponendo che un indice sia definito in PostId
, questa query è molto efficiente e non è sensibile anche alle modifiche simultanee che si verificano nei valori ID inferiori.
La paginazione keyset è appropriata per le interfacce di paginazione in cui l'utente passa avanti e indietro, ma non supporta l'accesso casuale, in cui l'utente può passare a una pagina specifica. L'impaginazione ad accesso casuale richiede l'uso dell'impaginazione offset come illustrato in precedenza; a causa delle carenze dell'impaginazione offset, valutare attentamente se l'impaginazione ad accesso casuale è effettivamente necessaria per il caso d'uso o se lo spostamento della pagina successiva/precedente è sufficiente. Se è necessaria un'impaginazione ad accesso casuale, un'implementazione affidabile potrebbe usare la paginazione keyset quando si passa alla pagina successiva/precedente e lo spostamento scostamento quando si passa a qualsiasi altra pagina.
Più chiavi di paginazione
Quando si usa la paginazione keyset, è spesso necessario ordinare in base a più proprietà. Ad esempio, la query seguente impagina per data e ID:
var lastDate = new DateTime(2020, 1, 1);
var lastId = 55;
var nextPage = context.Posts
.OrderBy(b => b.Date)
.ThenBy(b => b.PostId)
.Where(b => b.Date > lastDate || (b.Date == lastDate && b.PostId > lastId))
.Take(10)
.ToList();
In questo modo, la pagina successiva sceglie esattamente dove è terminata quella precedente. Man mano che vengono aggiunte altre chiavi di ordinamento, è possibile aggiungere clausole aggiuntive.
Nota
La maggior parte dei database SQL supporta una versione più semplice ed efficiente di quanto sopra, usando i valori di riga: WHERE (Date, Id) > (@lastDate, @lastId)
. EF Core attualmente non supporta l'espressione nelle query LINQ, che viene rilevata da #26822.
Indici
Come per qualsiasi altra query, l'indicizzazione corretta è fondamentale per ottenere prestazioni ottimali: assicurarsi di disporre di indici sul posto che corrispondono all'ordinamento della paginazione. Se l'ordinamento viene eseguito in base a più colonne, è possibile definire un indice su tali colonne; si tratta di un indice composito.
Per altre informazioni, vedere la pagina della documentazione sugli indici.
Risorse aggiuntive
- Per altre informazioni sulle carenze della paginazione basata su offset e sull'impaginazione keyset, vedere questo post.
- Sessione di standup della community di dati .NET in cui vengono illustrati i concetti di paginazione e demo di tutti i concetti precedenti.
- Presentazione di approfondimento tecnico che confronta l'offset e l'impaginazione keyset. Anche se il contenuto riguarda il database PostgreSQL, le informazioni generali sono valide anche per altri database relazionali.
- Per le estensioni su EF Core che semplificano l'impaginazione del keyset, vedere MR. EntityFrameworkCore.KeysetPagination e MR. AspNetCore.Pagination.
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: Nel corso del 2024 verranno gradualmente disattivati i problemi di GitHub come meccanismo di feedback per il contenuto e ciò verrà sostituito con un nuovo sistema di feedback. Per altre informazioni, vedereInvia e visualizza il feedback per