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:

  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 potenzialmente significativo che aumenta con il numero di righe ignorate.
  2. 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