Udostępnij za pośrednictwem


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:

  1. 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.
  2. 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.