Penentuan halaman

Pagination mengacu pada pengambilan hasil dalam bentuk halaman, daripada secara keseluruhan sekaligus; ini biasanya dilakukan untuk kumpulan hasil yang besar, di mana antarmuka pengguna ditampilkan yang memungkinkan pengguna menavigasi ke halaman hasil berikutnya atau sebelumnya.

Peringatan

Terlepas dari metode penomoran halaman yang digunakan, selalu pastikan bahwa pemesanan Anda sepenuhnya unik. Misalnya, jika hasil diurutkan hanya berdasarkan tanggal, tetapi mungkin ada beberapa hasil dengan tanggal yang sama, maka hasilnya dapat dilewati saat paginating karena diurutkan secara berbeda di dua kueri paginating. Pemesanan berdasarkan tanggal dan ID (atau properti unik atau kombinasi properti lainnya) membuat pemesanan sepenuhnya unik dan menghindari masalah ini. Perhatikan bahwa database relasional tidak menerapkan pengurutan apa pun secara default, bahkan pada kunci utama.

Catatan

Azure Cosmos DB memiliki mekanisme sendiri untuk pagination, lihat halaman dokumentasi yang khusus.

Penomoran halaman offset

Cara umum untuk menerapkan penomoran halaman dengan database adalah dengan menggunakan Skip operator LINQ dan Take (OFFSET dan LIMIT di SQL). Mengingat ukuran halaman 10 hasil, halaman ketiga dapat diambil dengan EF Core sebagai berikut:

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

Sayangnya, meskipun teknik ini sangat intuitif, teknik ini juga memiliki beberapa kekurangan yang parah:

  1. Database masih harus memproses 20 entri pertama, meskipun tidak dikembalikan ke aplikasi; ini menciptakan beban komputasi yang mungkin signifikan yang meningkat dengan jumlah baris yang dilewati.
  2. Jika ada pembaruan yang terjadi secara bersamaan, pengaturan halaman Anda mungkin akhirnya melewati entri tertentu atau menunjukkannya dua kali. Misalnya, jika entri dihapus saat pengguna berpindah dari halaman 2 ke 3, seluruh hasil "bergeser ke atas", dan satu entri akan dilewati.

Penomoran halaman dengan keyset

Alternatif yang direkomendasikan untuk paginasi berbasis offset - kadang-kadang disebut keyset pagination atau paginasi berbasis pencarian - adalah cukup menggunakan WHERE klausa untuk melewati baris, bukan offset. Ini berarti mengingat nilai-nilai yang relevan dari entri terakhir yang diambil (bukan offsetnya), dan meminta baris-baris berikutnya setelah entri tersebut. Misalnya, dengan asumsi entri terakhir di halaman terakhir yang kami ambil memiliki nilai ID 55, kami hanya akan melakukan hal berikut:

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

Dengan asumsi indeks didefinisikan pada PostId, kueri ini sangat efisien, dan juga tidak sensitif terhadap perubahan bersamaan yang terjadi dalam nilai Id yang lebih rendah.

Keyset pagination cocok untuk antarmuka penomoran halaman di mana pengguna menavigasi maju dan mundur, namun tidak mendukung akses acak, sehingga pengguna tidak dapat langsung melompat ke halaman tertentu. Paginasi akses acak memerlukan penggunaan paginasi offset seperti yang dijelaskan di atas; karena kekurangan paginasi offset, pertimbangkan dengan cermat apakah paginasi akses acak benar-benar diperlukan untuk kasus penggunaan Anda, atau jika navigasi halaman berikutnya/sebelumnya sudah cukup. Jika penomoran halaman akses acak diperlukan, implementasi yang kuat dapat menggunakan penomoran halaman keyset saat menavigasi ke halaman berikutnya/sebelumnya, dan mengimbangi navigasi saat melompat ke halaman lain.

Beberapa kunci penomoran halaman

Saat menggunakan keyset pagination, sering kali perlu untuk mengurutkan berdasarkan lebih dari satu properti. Misalnya, kueri berikut paginasi menurut tanggal dan 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();

Ini memastikan bahwa halaman berikutnya melanjutkan persis dari mana halaman sebelumnya berakhir. Karena lebih banyak kunci pengurutan ditambahkan, klausul tambahan dapat ditambahkan.

Catatan

Sebagian besar database SQL mendukung versi di atas yang lebih sederhana dan lebih efisien, menggunakan nilai baris: WHERE (Date, Id) > (@lastDate, @lastId). EF Core saat ini tidak mendukung mengekspresikan ini dalam kueri LINQ, ini dilacak oleh #26822.

Indeks

Seperti halnya kueri lain, pengindeksan yang tepat sangat penting untuk performa yang baik: pastikan untuk memiliki indeks yang sesuai dengan urutan penomoran halaman Anda. Jika mengurutkan menurut lebih dari satu kolom, indeks di atas beberapa kolom tersebut dapat ditentukan; ini disebut indeks komposit.

Untuk informasi selengkapnya, lihat halaman dokumentasi tentang indeks.

Sumber Daya Tambahan: