Delen via


Pagina-indeling

Paginering verwijst naar het ophalen van resultaten in pagina's, in plaats van allemaal tegelijk; Dit wordt meestal gedaan voor grote resultatensets, waarbij een gebruikersinterface wordt weergegeven waarmee de gebruiker naar de volgende of vorige pagina van de resultaten kan navigeren.

Waarschuwing

Ongeacht de gebruikte pagineringsmethode, zorg er altijd voor dat uw bestelling volledig uniek is. Als resultaten bijvoorbeeld alleen op datum worden gerangschikt, maar er meerdere resultaten met dezelfde datum kunnen zijn, kunnen resultaten worden overgeslagen wanneer ze anders worden gepagineerd in twee pagineringsquery's. Rangschikken op zowel datum als id (of een andere unieke eigenschap of combinatie van eigenschappen) maakt de volgorde volledig uniek en voorkomt dit probleem. Houd er rekening mee dat relationele databases geen standaardvolgorde toepassen, zelfs niet op de primaire sleutel.

Opmerking

Azure Cosmos DB heeft een eigen mechanisme voor paginering. Zie de speciale documentatiepagina.

Offsetpaginering

Een veelgebruikte manier om paginering met databases te implementeren, is door de Skip operators en Take LINQ-operators (OFFSET en LIMIT in SQL) te gebruiken. Op basis van een paginaformaat van 10 resultaten kan de derde pagina als volgt worden opgehaald met EF Core:

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

Helaas, hoewel deze techniek erg intuïtief is, heeft het ook een aantal ernstige tekortkomingen:

  1. De database moet nog steeds de eerste 20 vermeldingen verwerken, zelfs als ze niet worden geretourneerd naar de toepassing; dit creëert mogelijk een aanzienlijke rekenbelasting die toeneemt met het aantal rijen dat wordt overgeslagen.
  2. Als er gelijktijdig updates worden uitgevoerd, kan uw paginering bepaalde vermeldingen overslaan of twee keer weergeven. Als een item bijvoorbeeld wordt verwijderd terwijl de gebruiker van pagina 2 naar 3 overstapt, wordt de hele resultatenset 'omhoog verplaatst' en één vermelding overgeslagen.

Paginering van sleutelset

Het aanbevolen alternatief voor paginering op basis van offset, ook wel keysetpation of op zoek gebaseerde paginering genoemd, is door eenvoudigweg een WHERE component te gebruiken om rijen over te slaan, in plaats van een offset. Dit betekent dat u de relevante waarden van de laatste opgehaalde vermelding (in plaats van de offset) onthoudt en dat u na die rij om de volgende rijen moet vragen. Stel dat het laatste item op de laatste pagina die we hebben opgehaald, een id-waarde van 55 had, doen we het volgende:

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

Ervan uitgaande dat een index is gedefinieerd PostId, is deze query zeer efficiënt en is ook niet gevoelig voor gelijktijdige wijzigingen in lagere id-waarden.

Paginering van sleutelsets is geschikt voor pagineringsinterfaces waarbij de gebruiker vooruit en achteruit navigeert, maar geen willekeurige toegang ondersteunt, waarbij de gebruiker naar een specifieke pagina kan springen. Paginering van willekeurige toegang vereist het gebruik van offsetpaginering zoals hierboven wordt uitgelegd; Vanwege de tekortkomingen van offsetpaginering moet u zorgvuldig overwegen of paginering van willekeurige toegang echt vereist is voor uw use-case, of als de volgende/vorige paginanavigatie voldoende is. Als paginering van willekeurige toegang nodig is, kan een robuuste implementatie gebruikmaken van de paginering van de sleutelset bij het navigeren naar de volgende/vorige pagina en offsetnavigatie bij het springen naar een andere pagina.

Meerdere pagineringssleutels

Wanneer u sleutelsetpaginering gebruikt, is het vaak nodig om op meerdere eigenschappen te sorteren. De volgende query wordt bijvoorbeeld gepagineerd op datum en id:

var lastDate = new DateTime(2020, 1, 1);
var lastId = 55;
var nextPage = await context.Posts
    .OrderBy(b => b.Date)
    .ThenBy(b => b.PostId)
    .Where(b => b.Date > lastDate || (b.Date == lastDate && b.PostId > lastId))
    .Take(10)
    .ToListAsync();

Dit zorgt ervoor dat de volgende pagina precies verdergaat waar de vorige pagina is geëindigd. Naarmate er meer bestelsleutels worden toegevoegd, kunnen er extra clausules worden toegevoegd.

Opmerking

De meeste SQL-databases ondersteunen een eenvoudigere en efficiëntere versie van het bovenstaande, met behulp van rijwaarden: WHERE (Date, Id) > (@lastDate, @lastId). EF Core biedt momenteel geen ondersteuning voor het uitdrukken hiervan in LINQ-query's. Dit wordt bijgehouden door #26822.

Indexes

Net als bij elke andere query is de juiste indexering essentieel voor goede prestaties: zorg ervoor dat er indexen aanwezig zijn die overeenkomen met uw pagineringsvolgorde. Als u meerdere kolommen rangschikt, kan een index over die meerdere kolommen worden gedefinieerd; dit wordt een samengestelde index genoemd.

Zie de documentatiepagina over indexen voor meer informatie.

Aanvullende bronnen