Paginierung

Paginierung bedeutet, dass die Ergebnisse seitenweise abgerufen werden und nicht alle auf einmal. Dies geschieht in der Regel bei großen Ergebnismengen, bei denen eine Benutzeroberfläche angezeigt wird, über die der oder die Benutzer*in zur nächsten oder vorherigen Seite der Ergebnisse navigieren kann.

Warnung

Stellen Sie unabhängig von der verwendeten Paginierungsmethode immer sicher, dass Ihre Sortierung wirklich eindeutig ist. Wenn z. B. Ergebnisse nur nach Datum sortiert werden, aber es mehrere Ergebnisse mit demselben Datum geben kann, könnten die Ergebnisse beim Paginieren übersprungen werden, da sie in zwei Paginierungsabfragen unterschiedlich angeordnet sind. Durch Anordnung nach Datum und ID (oder einer anderen eindeutigen Eigenschaft oder Kombination von Eigenschaften) ist die Reihenfolge eindeutig, wodurch dieses Problem vermieden wird. Beachten Sie, dass in relationalen Datenbanken standardmäßig keine Anordnung gilt, auch nicht für den Primärschlüssel.

Offsetpaginierung

Eine gängige Methode zum Implementieren der Paginierung für Datenbanken ist die Verwendung von Skip und Take (OFFSET und LIMIT in SQL). Bei einer Seitengröße von zehn Ergebnissen kann die dritte Seite wie folgt mit EF Core abgerufen werden:

var position = 20;
var nextPage = context.Posts
    .OrderBy(b => b.PostId)
    .Skip(position)
    .Take(10)
    .ToList();

Obwohl diese Methode sehr intuitiv ist, hat sie auch einige schwerwiegende Nachteile:

  1. Die Datenbank muss weiterhin die ersten 20 Einträge verarbeiten, auch wenn sie nicht an die Anwendung zurückgegeben werden. Dies erzeugt möglicherweise eine erhebliche Rechenlast, die mit der Anzahl der übersprungenen Zeilen zunimmt.
  2. Wenn Änderungen gleichzeitig auftreten, kann die Paginierung bestimmte Einträge überspringen oder zweimal anzeigen. Wenn beispielsweise ein Eintrag entfernt wird, wenn der oder die Benutzer*in von Seite 2 zu Seite 3 wechselt, wird das gesamte Resultset „nach oben“ verschoben, und ein Eintrag würde übersprungen werden.

Keysetpaginierung

Die empfohlene Alternative zur offsetbasierten Paginierung , die manchmal Keysetpaginierung oder suchbasierte Paginierung genannt wird, besteht darin, eine WHERE-Klausel zum Überspringen von Zeilen anstelle eines Offsets zu verwenden. Das bedeutet, dass die relevanten Werte aus dem letzten abgerufenen Eintrag (anstelle des Offsets) gespeichert werden und die nächsten Zeilen nach dieser Zeile angefordert werden sollen. Wenn der letzte Eintrag auf der letzten Seite, den Sie abgerufen haben, beispielsweise einen ID-Wert von 55 hatte, führen Sie einfach die folgenden Schritte aus:

var lastId = 55;
var nextPage = context.Posts
    .OrderBy(b => b.PostId)
    .Where(b => b.PostId > lastId)
    .Take(10)
    .ToList();

Wenn ein Index für PostId definiert ist, ist diese Abfrage sehr effizient und wird nicht durch gleichzeitige Änderungen beeinträchtigt, die in niedrigeren ID-Werten auftreten.

Die Keysetpaginierung eignet sich für Paginierungsschnittstellen, bei denen Benutzer*innen vorwärts und rückwärts navigieren, aber unterstützt keinen wahlfreien Zugriff, wo Benutzer*innen zu einer bestimmten Seite springen können. Für die Paginierung mit wahlfreiem Zugriff ist die Verwendung der Offsetpaginierung erforderlich, wie oben erläutert. Aufgrund der Nachteile der Offsetpaginierung sollten Sie sorgfältig prüfen, ob die Paginierung mit wahlfreiem Zugriff wirklich für Ihren Anwendungsfall erforderlich ist oder ob die Navigation zur nächsten/vorherigen Seite ausreichend ist. Wenn die Paginierung mit wahlfreiem Zugriff erforderlich ist, kann eine robuste Implementierung die Keysetpaginierung verwenden, wenn die Navigation zur nächsten/vorherigen Seite erfolgt, und die Offsetnavigation, wenn zu einer anderen Seite gewechselt wird.

Mehrere Paginierungsschlüssel

Bei Verwendung der Keysetpaginierung ist es häufig erforderlich, nach mehreren Eigenschaften zu sortieren. Die folgende Abfrage paginiert z. B. nach Datum und 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();

Dadurch wird sichergestellt, dass die nächste Seite genau an der Stelle weitergeht, an der die vorherige Seite aufgehört hat. Wenn weitere Sortierschlüssel hinzugefügt werden, können zusätzliche Klauseln hinzugefügt werden.

Hinweis

Die meisten SQL-Datenbanken unterstützen eine einfachere und effizientere Version, bei der Zeilenwerte verwendet werden: WHERE (Date, Id) > (@lastDate, @lastId). EF Core unterstützt derzeit keine Ausdrücke in LINQ-Abfragen. Dies wird in #26822 nachverfolgt.

Indizes

Wie bei jeder anderen Abfrage ist die ordnungsgemäße Indizierung für eine gute Leistung von entscheidender Bedeutung. Stellen Sie sicher, dass Indizes vorhanden sind, die Ihrer Paginierungsreihenfolge entsprechen. Wenn nach mehr als einer Spalte sortiert wird, kann ein Index für diese Spalten definiert werden. Ein solcher Index wird als zusammengesetzter Index bezeichnet.

Weitere Informationen finden Sie in der Dokumentation zur Paginierung.

Zusätzliche Ressourcen