TGViewer
C# Geeks (.NET) C# Geeks (.NET) @csharpgeeks · 548 subscribers
Post #181 86
بحث Pagination در ASP.NET Core: Offset در برابر Cursor 📄🆚🚀


صفحه بندی برای مدیریت کارآمد مجموعه داده‌های بزرگ، حیاتی است. با اینکه offset pagination به طور گسترده استفاده می‌شود و کار را راه می‌اندازد، cursor-based pagination مزایای جالبی برای سناریوهای خاص ارائه می‌دهد.

این روش به ویژه برای فیدهای real-time، اینترفیس‌های infinite scroll، و APIهایی که عملکرد در مقیاس بالا در آن‌ها اهمیت دارد، ارزشمند است - مانند تایم‌لاین‌های شبکه‌های اجتماعی، لاگ‌های فعالیت، یا جریان‌های رویداد که کاربران به طور مکرر در صفحات مجموعه داده‌های بزرگ جابجا می‌شوند.

بیایید هر دو رویکرد را با استفاده از یک جدول ساده UserNotes بررسی کرده و ببینیم چگونه با یک میلیون رکورد عمل می‌کنند.

اسکیمای دیتابیس 💾

من یک جدول ساده برای نمایش تکنیک‌های pagination ایجاد کردم. این جدول با ۱,۰۰۰,۰۰۰ رکورد برای اهداف تست پر شده است، که باید برای نشان دادن تفاوت عملکرد بین offset و cursor pagination کافی باشد.
CREATE TABLE user_notes (
id uuid NOT NULL,
user_id uuid NOT NULL,
note character varying(500),
date date NOT NULL,
CONSTRAINT pk_user_notes PRIMARY KEY (id)
);

و این هم کلاس #C که انتیتی UserNote را نشان می‌دهد:
public class UserNote
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string? Note { get; set; }
public DateOnly Date { get; set; }
}


Offset Pagination: رویکرد سنتی 🐢

ما در Offset pagination از عملیات Skip و Take استفاده می‌کنیم. ما تعداد معینی از ردیف‌ها را رد می‌کنیم (skip) و تعداد ثابتی از ردیف‌ها را برمی‌داریم (take). این‌ها معمولاً به OFFSET و LIMIT در کوئری‌های SQL ترجمه می‌شوند.
app.MapGet("/offset", async (
AppDbContext dbContext,
int page = 1,
int pageSize = 10,
CancellationToken cancellationToken = default) =>
{
// ... (بررسی ورودی‌ها) ...
var query = dbContext.UserNotes
.OrderByDescending(x => x.Date)
.ThenByDescending(x => x.Id);

// Offset pagination معمولاً تعداد کل آیتم‌ها را می‌شمارد
var totalCount = await query.CountAsync(cancellationToken);
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);

// رد کردن و برداشتن تعداد مورد نیاز از آیتم‌ها
var items = await query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync(cancellationToken);

return Results.Ok(new { /* ... نتایج ... */ });
});


SQL تولید شده:
-- این کوئری اول ارسال می‌شود
SELECT count(*)::int FROM user_notes AS u;

-- و سپس کوئری اصلی داده‌ها
SELECT u.id, u.date, u.note, u.user_id
FROM user_notes AS u
ORDER BY u.date DESC, u.id DESC
LIMIT @pageSize OFFSET @offset;


محدودیت‌های Offset Pagination: ❌


• عملکرد با افزایش آفست کاهش می‌یابد زیرا دیتابیس باید تمام ردیف‌های قبل از آفست را اسکن و دور بریزد.

• ریسک از دست دادن یا تکرار آیتم‌ها وقتی داده‌ها بین صفحات تغییر می‌کنند.

• نتایج متناقض با آپدیت‌های همزمان.

Cursor-Based Pagination: یک رویکرد سریع‌تر 🚀


و حالا Cursor pagination از یک نقطه مرجع (cursor) برای واکشی مجموعه بعدی نتایج استفاده می‌کند. این نقطه مرجع معمولاً یک شناسه منحصر به فرد یا ترکیبی از فیلدهاست که ترتیب مرتب‌سازی را تعریف می‌کند.
app.MapGet("/cursor", async (
AppDbContext dbContext,
DateOnly? date = null,
Guid? lastId = null,
int limit = 10,
CancellationToken cancellationToken = default) =>
{
// ... (بررسی ورودی‌ها) ...
var query = dbContext.UserNotes.AsQueryable();

if (date != null && lastId != null)
{
// از cursor برای واکشی مجموعه بعدی نتایج استفاده می‌کنیم
query = query.Where(x => x.Date < date || (x.Date == date && x.Id <= lastId));
}

// آیتم‌ها را واکشی کرده و مشخص می‌کنیم آیا آیتم‌های بیشتری وجود دارد یا نه
var items = await query
.OrderByDescending(x => x.Date)
.ThenByDescending(x => x.Id)
.Take(limit + 1)
.ToListAsync(cancellationToken);

// ... (منطق برای استخراج cursor بعدی) ...

return Results.Ok(new { /* ... نتایج ... */ });
});
More from @csharpgeeks
  1. Sep 22, 2026یه مدتی قراره از دنیای NET. فاصله بگیرم، چون وقتشه برم سربازی. راستش نمیدونم این مدت رو چج…
  2. Sep 20, 2026🔥 حالا مشکل اصلی: Alert Storm فرض کن Database از دسترس خارج شده. ۱۰۰ Pod داری. هر Pod می‌…
  3. Sep 20, 2026🚨 طراحی سیستم Monitoring و Alerting در یک سیستم بزرگ فرض کن ساعت ۳ صبح است. سیستم شما با…
  4. Sep 19, 2026#Engineering_Leadership تصمیم نگرفتن هم یک تصمیم است یه چیز عجیب توی تیم‌های مهندسی: گاهی…
  5. Sep 19, 2026☑ چک‌لیست آماده‌سازی تیم، فرایندها و زیرساخت برای توسعه با AI توجه: هیچ چک‌لیستی جهان‌شمول…
  6. Sep 19, 2026📌پایان یک انتظار طولانی: اعتبارسنجی ناهمگام (Async Validation) در NET 11.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →