Overview
PagedListResult is a focused .NET library that standardizes server‑side pagination, sorting, searching, and filtering into a clear request → execution → result pipeline. The repository is intentionally opinionated: instead of exposing many small helpers, it defines strong contracts (request and result models) and a set of query extensions that apply those contracts to IQueryable sources.
The primary goal of the project is to eliminate repetitive pagination logic in APIs and application services, while keeping the query behavior explicit, testable, and extensible.
Design Philosophy
The repository follows several consistent design principles that are visible across all projects:
1. Contract‑first pagination – Pagination is driven by a request model rather than ad‑hoc parameters.
2. Queryable‑centric execution – All operations are applied to IQueryable, allowing deferred execution and provider‑level optimizations.
3. Separation of concerns – Request/response models, query logic, and common helpers are separated into distinct projects.
4. API‑oriented output – The result model includes metadata required by front‑end grids and consumers, not just the data subset.
Repository Structure
The solution is split into multiple projects, each with a strictly scoped responsibility. This separation is not cosmetic; it directly reflects how pagination concerns are decomposed and enforced across the library.
1. PagedListResult.DataModels (Core Contract Layer)
This project is the foundation of the entire solution. Every other project depends on it, and it intentionally contains no query execution logic. Its sole purpose is to define stable, serializable contracts that describe what the consumer wants and what the system returns.
Architectural Role
Acts as a *boundary layer** between clients (API/UI) and query execution
Ensures pagination behavior is explicit and versionable
Makes pagination requests portable across services
This design allows the same request model to be reused in:
ASP.NET Core controllers
Application services
Background jobs
Integration tests
Key Types and Responsibilities
PagedRequest
The PagedRequest type is the central aggregation root of pagination input. Rather than scattering parameters, it consolidates all query intent into a single object.
Core responsibilities:
Page index normalization
Page size definition
Optional ordering definition
Optional search definition
Optional filter collection
The model is intentionally permissive: most properties are optional, allowing minimal or advanced usage with the same contract.
Paging Semantics
Paging is expressed in logical terms, not execution terms:
Pagerepresents the requested page indexPageSizerepresents the logical size of the page
No assumptions are made about storage or transport. Execution layers decide how to translate these into Skip / Take operations.
OrderOptions
OrderOptions defines ordering intent without binding to LINQ or database specifics.
Key characteristics:
Property name is expressed as a string
Direction is represented as a semantic enum or value
Designed to be validated before execution
This allows clients to control ordering while keeping execution safe and predictable.
SearchOptions
Search is modeled explicitly rather than as a generic filter.
Responsibilities:
Store the search term
Define whether the search applies to all text fields
Enable consistent interpretation of search intent
By isolating search into its own model, the library avoids overloading filters with responsibilities they are not suited for.
Filter Models
Filtering is expressed through a structured hierarchy rather than ad-hoc predicates.
Typical filter elements include:
Target property name
Condition operator (equality, range, containment, etc.)
One or more comparison values
ANDorORcomparation apply between values/filtersDependence between filters
This structure enables:
Validation before execution
Deterministic expression generation
Consistent behavior across endpoints
PagedResult
PagedResult is the authoritative output contract of the library.
It contains:
The paged response collection
Paging metadata (page, page size, page count)
Total row count
Execution diagnostics
Importantly, the result is self-describing: consumers do not need to recompute or infer paging state.
ExecutionDetails
Execution diagnostics are modeled explicitly rather than logged implicitly.
This enables:
Performance analysis at the API boundary
Transparent reporting to consumers
Easier debugging in distributed systems
2. PagedListResult.Common (Query Interpretation Layer)
This project translates intent into executable logic.
Its responsibility is to convert DataModel contracts into expression trees that can be applied to IQueryable without knowing the underlying provider.
Core Responsibilities
Validate property access at runtime
Build expression trees for:
Filters
Search
Ordering
Combine multiple expressions deterministically
Expression-Driven Design
Rather than building queries imperatively, the library constructs expression trees. This allows:
Provider-level optimization
Deferred execution
Compatibility with LINQ providers
Each query concern (filter, search, order) is handled independently and then composed.
3. PagedListResult (Execution & Integration Layer)
This project is the behavioral core of the solution. While DataModels defines what should happen and Common defines how intent is translated, PagedListResult defines when and in what order everything is executed.
It is also the only project that consumers typically reference directly.
Public API Surface
The project exposes a minimal but powerful public surface:
Extension methods over
IQueryableStrongly typed pagination execution returning
PagedResult
This design deliberately avoids:
Base repository abstractions
Framework‑specific dependencies at the API boundary
Hidden execution side effects
Consumers remain in full control of when queries are materialized.
Extension Method Architecture
Pagination is implemented entirely via extension methods rather than services or static helpers.
Key implications:
Pagination composes naturally with LINQ
No dependency injection is required
Domain entities remain untouched
A typical execution entry point:
query.GetPagedAsync(pagedRequest);
query.GetPagedWithFiltersAsync(pagedRequest);
query.GetPagedWithMainFiltersAsync(pagedRequest);Internally, this single call coordinates the entire pipeline.
Execution Orchestration Responsibilities
The PagedListResult project does not build expressions itself. Instead, it orchestrates execution by delegating responsibilities in a strict order.
Its responsibilities include:
1. Guard‑clause validation of the request
2. Delegation of search, filter, and order logic
3. Pagination window calculation
4. Execution diagnostics capture
5. Final result assembly
This ensures that query semantics remain centralized and predictable.
Ordering Strategy Enforcement
One subtle but critical responsibility of this layer is ordering enforcement.
Stable pagination requires deterministic ordering. The execution layer ensures that:
* Explicit ordering is applied when provided
* A fallback strategy exists when ordering is missing
Ordering is applied before* pagination
Without this enforcement, page boundaries could shift between executions.
Row Count Evaluation
Row count is evaluated before pagination is applied.
This design decision ensures:
* Accurate RowCount
* Correct PageCount
* Independence from page size or index
The execution layer explicitly separates:
* Total dataset size
* Paged window size
This distinction is critical for client‑side pagination controls.
Pagination Window Calculation
The execution layer translates logical paging intent into execution behavior:
Calculates skip offset from page index and page size
Applies
SkipandTakeonly after all query conditions
This guarantees that pagination is applied to the final filtered and ordered dataset.
ExecutionDetails Population
Execution diagnostics are captured as part of the core workflow.
Typical captured details include:
Start and end timestamps
Total execution duration
By embedding diagnostics in the result rather than logging implicitly, the library enables:
Transparent performance reporting
API‑level observability
Easier troubleshooting without log access
Result Assembly
The final step is deterministic result construction.
The execution layer assembles:
Materialized response data
Paging metadata
Execution diagnostics
The resulting PagedResult is immediately consumable by API clients without additional transformation.
Detailed Pagination Execution Pipeline
The execution pipeline is deterministic and ordered to avoid ambiguity.
1. Request Validation
* Page boundaries
* Page size constraints
* Property existence for filters and ordering
2. Search Application
* Text-based expressions generated first
* Applied before filters to reduce dataset early
3. Filter Application
* Structured conditions translated into expressions
* Combined using logical AND or OR semantics
4. Ordering Application
* Explicit ordering or fallback strategy
* Ensures stable pagination
5. Row Count Evaluation
* Executed before paging
* Represents total matching dataset size
6. Pagination Application
* Skip and Take applied last
7. Result Materialization
* Execution timing captured
* PagedResult populated
Structured Result Serialization: JSON and XML Support
One important capability of the PagedListResult project that deserves explicit attention is its built‑in support for result serialization targeting JSON and XML consumers.
This functionality is not an afterthought; it is reflected directly in the source code and aligns with the library’s API‑first design.
Why Serialization Is a First‑Class Concern
The library assumes that paged results are most often:
Returned from REST APIs
Consumed by heterogeneous clients
Persisted or logged in structured formats
For that reason, the result model PagedResult) and its related metadata types are designed to be:
Fully serializable
Deterministic in structure
Independent of execution context
This makes the result suitable for both JSON‑based APIs and XML‑based integrations without custom mapping layers.
JSON Result Shape
From the source perspective, the JSON representation is a direct projection of the result contract:
Paging metadata is exposed as top‑level fields
Execution details are explicitly represented
The response collection is serialized as a structured array
Because the library avoids custom converters or runtime mutation, the JSON output remains:
Stable across executions
Predictable for front‑end grids
Easy to version
This is particularly important for client‑side pagination components that depend on fixed property names.
XML Result Support
The repository also supports XML‑friendly result structures.
Key characteristics visible in the source:
Result and metadata models are designed with XML serialization compatibility in mind
No reliance on JSON‑only attributes or behaviors
Clean hierarchical structure suitable for XML consumers
This enables scenarios such as:
Legacy system integrations
SOAP‑adjacent services
Inter‑service communication where XML is still required
The same PagedResult contract can be serialized to XML without transformation, preserving paging semantics and metadata.
Search Behavior and Serialization Alignment
Search functionality (including text search across multiple fields) is intentionally decoupled from serialization concerns.
However, the results of search operations are fully reflected in the serialized output:
RowCount represents the count after search and filters*
PageCountreflects the searched datasetThe response collection contains only matching items
This guarantees that JSON or XML consumers always receive a consistent and truthful representation of the executed query.
Design Implications
By designing serialization‑friendly result contracts, the library achieves:
Zero transformation overhead at API boundaries
Reduced controller/service boilerplate
Clear separation between execution logic and representation
This reinforces the repository’s broader architectural theme: explicit contracts over implicit behavior.
Summary
PagedListResult is not only a pagination and query‑execution library; it is also a transport‑ready result framework.
It's built‑in alignment with JSON and XML serialization ensures that paged results can move safely and consistently across system boundaries, making it especially suitable for real‑world API and integration scenarios.
For teams working with mixed client ecosystems or strict API contracts, this aspect of the library is a critical—and often underappreciated—strength.
PagedListResult is not a simple paging helper; it is a contract-driven pagination framework designed for service-oriented .NET applications.
By separating:
intent (DataModels)
interpretation (Common)
execution (Core)
…the repository achieves a high level of clarity, safety, and extensibility.
For teams building APIs that require consistent, transparent, and extensible pagination behavior, this project provides a strong and thoughtfully designed foundation.
PagedListResult is a strongly structured pagination solution for .NET developers who want:
Explicit pagination contracts
Centralized query logic
Rich response metadata
Minimal repetition across endpoints
The repository demonstrates a clean separation between data contracts, query infrastructure, and execution logic, making it both approachable for consumers and extensible for advanced scenarios.
For developers building serious, data‑driven APIs, this repository provides a solid and opinionated foundation for pagination done right.
Repository: https://github.com/I-RzR-I/PagedListResult
