Podział na strony
Stronicowanie odnosi się do pobierania wyników na stronach, a nie wszystkich jednocześnie; Zazwyczaj jest to wykonywane w przypadku dużych zestawów wyników, w których jest wyświetlany interfejs użytkownika, który umożliwia użytkownikowi przejście do następnej lub poprzedniej strony wyników.
Ostrzeżenie
Niezależnie od używanej metody stronicowania, zawsze upewnij się, że kolejność jest w pełni unikatowa. Jeśli na przykład wyniki są uporządkowane tylko według daty, ale może istnieć wiele wyników z tą samą datą, wyniki można pominąć podczas stronicowania, ponieważ są one uporządkowane inaczej w dwóch zapytaniach stronicowania. Kolejność według daty i identyfikatora (lub dowolnej innej unikatowej właściwości lub kombinacji właściwości) sprawia, że kolejność jest w pełni unikatowa i pozwala uniknąć tego problemu. Należy pamiętać, że relacyjne bazy danych domyślnie nie stosują żadnych zamówień, nawet w kluczu podstawowym.
Uwaga
Usługa Azure Cosmos DB ma własny mechanizm stronicowania, zobacz dedykowaną stronę dokumentacji.
Stronicowanie przesunięcia
Typowym sposobem implementacji stronicowania z bazami danych jest użycie Skip
Take
operatorów LINQ (OFFSET
i LIMIT
w języku SQL). Biorąc pod uwagę rozmiar strony 10 wyników, trzecią stronę można pobrać za pomocą programu EF Core w następujący sposób:
var position = 20;
var nextPage = context.Posts
.OrderBy(b => b.PostId)
.Skip(position)
.Take(10)
.ToList();
Niestety, chociaż ta technika jest bardzo intuicyjna, ma również pewne poważne wady:
- Baza danych musi nadal przetwarzać pierwsze 20 wpisów, nawet jeśli nie zostaną zwrócone do aplikacji; spowoduje to prawdopodobnie znaczne obciążenie obliczeniowe, które zwiększa się wraz z liczbą pominiętych wierszy.
- Jeśli jakiekolwiek aktualizacje wystąpią współbieżnie, stronicowanie może zakończyć się pomijaniem niektórych wpisów lub wyświetlaniem ich dwa razy. Jeśli na przykład wpis zostanie usunięty, ponieważ użytkownik przechodzi ze strony 2 do 3, cały zestaw wyników "przesuwa się w górę", a jeden wpis zostanie pominięty.
Stronicowanie zestawu kluczy
Zalecaną alternatywą dla stronicowania opartego na przesunięciach — czasami nazywaną stronicacją zestawu kluczy lub stronicacją opartą WHERE
na wyszukiwaniu — jest po prostu użycie klauzuli do pomijania wierszy zamiast przesunięcia. Oznacza to, że należy pamiętać odpowiednie wartości z ostatniego pobranego wpisu (zamiast przesunięcia) i poprosić o kolejne wiersze po tym wierszu. Na przykład przy założeniu, że ostatni wpis na ostatniej pobranej stronie miał wartość identyfikatora 55, po prostu wykonalibyśmy następujące czynności:
var lastId = 55;
var nextPage = context.Posts
.OrderBy(b => b.PostId)
.Where(b => b.PostId > lastId)
.Take(10)
.ToList();
Zakładając, że indeks jest zdefiniowany w elemecie PostId
, to zapytanie jest bardzo wydajne, a także nie jest wrażliwe na żadne współbieżne zmiany w niższych wartościach identyfikatorów.
Stronicowanie zestawu kluczy jest odpowiednie dla interfejsów stronicowania, w których użytkownik przechodzi do przodu i do tyłu, ale nie obsługuje dostępu losowego, gdzie użytkownik może przejść do dowolnej konkretnej strony. Stronicowanie dostępu losowego wymaga użycia stronicowania przesunięcia, jak wyjaśniono powyżej; z powodu niedociągnięć stronicowania przesunięcia należy dokładnie rozważyć, czy stronicowanie dostępu losowego jest naprawdę wymagane dla twojego przypadku użycia, lub jeśli nawigacja na następnej/poprzedniej stronie jest wystarczająca. Jeśli jest konieczne stronicowanie dostępu losowego, niezawodna implementacja może używać stronicowania zestawu kluczy podczas nawigacji do następnej/poprzedniej strony i przesunięcia nawigacji podczas przechodzenia do dowolnej innej strony.
Wiele kluczy stronicowania
W przypadku używania stronicowania zestawu kluczy często jest konieczne zamawianie przez więcej niż jedną właściwość. Na przykład następujące zapytanie stronicuje według daty i identyfikatora:
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();
Dzięki temu następna strona zostanie wybrana dokładnie tam, gdzie zakończyła się poprzednia strona. W miarę dodawania większej liczby kluczy porządkowania można dodać dodatkowe klauzule.
Uwaga
Większość baz danych SQL obsługuje prostszą i wydajniejszą wersję powyższych elementów przy użyciu wartości wierszy: WHERE (Date, Id) > (@lastDate, @lastId)
. Program EF Core nie obsługuje obecnie wyrażania tego w zapytaniach LINQ. Jest to śledzone przez program #26822.
Indeksy
Podobnie jak w przypadku każdego innego zapytania, odpowiednie indeksowanie jest niezbędne dla dobrej wydajności: upewnij się, że indeksy mają miejsce, które odpowiadają kolejności stronicowania. W przypadku porządkowania według więcej niż jednej kolumny można zdefiniować indeks na tych wielu kolumnach; jest to nazywane indeksem złożonym.
Aby uzyskać więcej informacji, zobacz stronę dokumentacji na temat indeksów.
Dodatkowe zasoby
- Aby dowiedzieć się więcej na temat niedociągnięć stronicowania opartego na przesunięciach i stronicowania zestawu kluczy, zobacz ten wpis.
- Sesja standupu społeczności danych platformy .NET, w której omawiamy stronicowanie i pokaz wszystkich powyższych pojęć.
- Prezentacja szczegółowej analizy technicznej porównująca przesunięcie i stronicowanie zestawu kluczy. Chociaż zawartość dotyczy bazy danych PostgreSQL, ogólne informacje są prawidłowe również dla innych relacyjnych baz danych.
- Aby uzyskać rozszerzenia na platformie EF Core, które upraszczają stronicowanie zestawu kluczy, zobacz MR. EntityFrameworkCore.KeysetPagination i MR. AspNetCore.Pagination.