Skip to content

BuildingBlocks.Pagination.EntityFrameworkCore 1.1.0

Choose a tag to compare

@Maxofpower Maxofpower released this 04 Sep 17:10
· 32 commits to main since this release

Summary

Index-friendlier keyset pagination: PostgreSQL multi-column row comparison, ORDER BY NULLS FIRST/LAST end-to-end where supported, optional matching index null-sort metadata, and aligned Dapper SQL generation.

NuGet

BuildingBlocks.Pagination.EntityFrameworkCore 1.1.0

dotnet add package BuildingBlocks.Pagination.EntityFrameworkCore --version 1.1.0

Features added

EF Core (packaged)

  • Npgsql row-comparison seek for uniform non-nullable multi-column keys of any width ((a, b, …) > … via soft-resolved EF.Functions.GreaterThan / LessThan).
    • 2–8 columns: ValueTuple.Create
    • 9+: nested ValueTuple TRest
    • Mixed ASC/DESC, string slots, Sqlite, and SQL Server keep the expanded OR seek
  • AddBuildingBlocksPagination() + UseBuildingBlocksPagination() — DbCommandInterceptor appends NULLS FIRST/LAST on Npgsql/Sqlite at execute time
    • Placement encoded in query tag BuildingBlocks.Pagination:First|Last (inverted on backward walks)
    • Not AsyncLocal; Add-without-Use stays 1.0-compatible (seek-only NullOrder)
    • ToQueryString() does not run interceptors — assert executed SQL for NULLS
  • HasKeysetIndex(sortKey, NullOrder) — soft call to Npgsql HasNullSortOrder so index NULLS can match ORDER BY … NULLS …
    • One-argument HasKeysetIndex(sortKey) does not write null-sort metadata
    • Npgsql omits NULLS from CREATE INDEX when it matches the column ASC/DESC default
  • EF seek null comparisons for reference/string slots align with true NULLS FIRST/LAST semantics (fixes end-to-end null ordering with the interceptor)

IR (bundled in the same nupkg)

  • NullOrder contract documented as seek and ORDER BY null placement where supported (was seek-predicate only in 1.0)
  • SeekOps helpers for tuple eligibility / non-null slots used by EF and Dapper

Dapper (in-repo project, not a separate NuGet)

  • ORDER BY … NULLS FIRST/LAST on PostgreSQL and Sqlite from PaginationOptions.Nulls (inverted on backward walks)
  • SQL Server still omits NULLS
  • Existing Postgres tuple SQL for uniform directions remains; coverage extended for wider keys (3 / 4 / 9 columns)

Lab (FeatureFusion)

  • NameThenPrice product sort (3-column keyset: Name, Price, Id)
  • Registers AddBuildingBlocksPagination + UseBuildingBlocksPagination
  • Matching indexes / migration for the new sort

Tests / CI

  • EF Postgres Testcontainers: 2-, 3-, 4-, 9-column tuple SQL shape; string/mixed stay OR; NULLS via interceptor
  • Model-only HasKeysetIndex → CREATE INDEX contains non-default NULLS FIRST
  • Dapper: NULLS on PG/Sqlite, no NULLS on SQL Server; multi-column tuple SQL
  • pagination.yml: PAGINATION_POSTGRES_REQUIRED — skipped Postgres tests fail CI

Host checklist

  1. Upgrade to 1.1.0 (drop-in for existing ToCursorPageAsync / OR seek).
  2. For EF ORDER BY … NULLS …: call both services.AddBuildingBlocksPagination() and options.UseBuildingBlocksPagination().
  3. Optionally HasKeysetIndex(key, NullOrder.First|Last) when NULLS is not the ASC/DESC default.
  4. Host must already reference Npgsql.EntityFrameworkCore.PostgreSQL for tuple seek / index null-sort (no hard PackageReference in the nupkg).

Unchanged / non-goals in 1.1.0

  • Nullable value-type sort slots (int?, DateTime?, …) still throw NullableSortUnsupported
  • SQL Server still does not emit NULLS on ORDER BY
  • Dapper remains an in-repo project (not packed)

Docs

What's Changed

Full Changelog: idempotency-v1.0.0...pagination-v1.1.0