Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warnings for deterministic ordering #3685

Merged
merged 4 commits into from
Jan 31, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions entity-framework/core/querying/pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ uid: core/querying/pagination

Pagination refers to retrieving results in pages, rather than all at once; this is typically done for large resultsets, where a user interface is shown that allows the user to navigate to the next or previous page of the results.

> [!WARNING]
> Regardless of the pagination method used, always make sure that your ordering is fully deterministic. For example, if results are ordered only by date, but there can be multiple results with the same date, then results could be skipped when paginating as they're ordered differently across two paginating queries. Ordering by both date and ID (or any other unique property) makes the ordering fully deterministic and avoids this problem. Note that relational databases do not apply any ordering by default, even on the primary key.
ajcvickers marked this conversation as resolved.
Show resolved Hide resolved

## Offset pagination

A common way to implement pagination with databases is to use the `Skip` and `Take` (`OFFSET` and `LIMIT` in SQL). Given a a page size of 10 results, the third page can be fetched with EF Core as follows:
Expand All @@ -30,9 +33,6 @@ Assuming an index is defined on `PostId`, this query is very efficient, and also

Keyset pagination is appropriate for pagination interfaces where the user navigates forwards and backwards, but does not support random access, where the user can jump to any specific page. Random access pagination requires using offset pagination as explained above; because of the shortcomings of offset pagination, carefully consider if random access pagination really is required for your use case, or if next/previous page navigation is enough. If random access pagination is necessary, a robust implementation could use keyset pagination when navigation to the next/previous page, and offset navigation when jumping to any other page.

> [!WARNING]
> Always make sure that your ordering is fully deterministic. For example, if results are ordered only by date, but there can be multiple results with the same date, then results could be skipped when paginating as they're ordered differently across two queries. Ordering by both date and ID (or any other unique property) makes the resultset deterministic and avoids this problem. Note that relational databases do not apply any ordering by default, even on the primary key; queries without explicit ordering have non-deterministic resultsets.

### Multiple pagination keys

When using keyset pagination, it's frequently necessary to order by more than one property. For example, the following query paginates by date and ID:
Expand Down
3 changes: 3 additions & 0 deletions entity-framework/core/querying/single-split-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ INNER JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
```

> [!WARNING]
> When using split queries with Skip/Take, pay special attention to making your query ordering fully deterministic; not doing so could cause incorrect data to be returned. For example, if results are ordered only by date, but there can be multiple results with the same date, then each one of the split queries could each get different results from the database. Ordering by both date and ID (or any other unique property) makes the ordering fully deterministic and avoids this problem. Note that relational databases do not apply any ordering by default, even on the primary key.
ajcvickers marked this conversation as resolved.
Show resolved Hide resolved

> [!NOTE]
> One-to-one related entities are always loaded via JOINs in the same query, as it has no performance impact.

Expand Down