Paginazione

La paginazione fa riferimento al recupero dei risultati nelle pagine, anziché a tutte contemporaneamente; questa operazione viene in genere eseguita per i 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'ordinamento 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 quando vengono impaginati in modo diverso tra due query di impaginazione. L'ordinamento per data e ID (o qualsiasi altra proprietà univoca o combinazione di proprietà) rende l'ordinamento completamente univoco ed evita questo problema. Si noti che i database relazionali non applicano alcun ordinamento per impostazione predefinita, anche sulla chiave primaria.

Impaginazione offset

Un modo comune per implementare la impaginazione con i database consiste nell'usare Skip e (OFFSET e TakeLIMIT in SQL). Dato 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, mentre questa tecnica è molto intuitiva, ha anche alcune gravi carenze:

  1. 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 significativo che aumenta con il numero di righe ignorate.
  2. Se si verificano aggiornamenti simultanei, la paginazione potrebbe saltare alcune voci o mostrarle due volte. Ad esempio, se una voce viene rimossa perché l'utente passa dalla pagina 2 a 3, l'intero set di risultati "sposta verso l'alto" e una voce verrà ignorata.

Paginazione keyset

L'alternativa consigliata alla paginazione basata su offset, talvolta denominata paginazione del set di chiavi o paginazione basata su ricerca , consiste nell'usare semplicemente una WHERE clausola per ignorare le righe anziché un offset. Ciò significa ricordare i valori pertinenti dell'ultima voce recuperata (anziché la relativa offset) e chiedere le righe successive dopo tale riga. Si supponga, ad esempio, che l'ultima voce dell'ultima pagina recuperata avesse 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 anche non è sensibile ad eventuali modifiche simultanee che si verificano nei valori ID inferiori.

La paginazione keyset è appropriata per le interfacce di impaginazione in cui l'utente passa avanti e indietro, ma non supporta l'accesso casuale, in cui l'utente può passare a qualsiasi pagina specifica. La paginazione di accesso casuale richiede l'uso della paginazione offset, come illustrato sopra; a causa delle carenze della paginazione di offset, valutare attentamente se la paginazione di accesso casuale è effettivamente necessaria per il caso d'uso o se lo spostamento della pagina successiva/precedente è sufficiente. Se è necessaria una paginazione di accesso casuale, un'implementazione affidabile potrebbe usare la pagina del set di chiavi quando si passa alla pagina successiva/precedente e lo spostamento offset quando si passa a qualsiasi altra pagina.

Più chiavi di impaginazione

Quando si usa la paginazione keyset, è spesso necessario ordinare più proprietà. Ad esempio, le query seguenti impaginano in base alla data e all'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();

Ciò garantisce che la pagina successiva sia selezionata esattamente dove è stata terminata la 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 e più efficiente di quella precedente, usando i valori di riga: WHERE (Date, Id) > (@lastDate, @lastId). EF Core attualmente non supporta l'espressione in query LINQ, questa operazione viene rilevata da #26822.

Indici

Come per qualsiasi altra query, l'indicizzazione appropriata è fondamentale per prestazioni ottimali: assicurarsi di avere indici sul posto che corrispondono all'ordine di impaginazione. Se l'ordinamento di più colonne può essere definito da più colonne, è possibile definire un indice su tali colonne. viene chiamato indice composito.

Per altre informazioni, vedere la pagina della documentazione sugli indici.

Risorse aggiuntive