Note: This repository targets
netstandard2.0(andnetstandard2.1for the ASP.NET Core integration) and runs on .NET 5, 6, 7, 8 and 9.
| Name | NuGet |
|---|---|
| RzR.ResultMessage.Pagination.Abstractions | |
| RzR.ResultMessage.Pagination.Core | |
| RzR.ResultMessage.Pagination.EntityFrameworkCore | |
| RzR.ResultMessage.Pagination.AspNetCore |
The repository started from a very common need: building server-side pagination for grids and tables (page, page size, search, ordering, filters, predefined "pinned" rows) without re-inventing the request and response shapes in every project. It has grown into a small set of layered libraries you can pick and choose from.
| Project | What it gives you |
|---|---|
PagedListResult.DataModels -> (RzR.ResultMessage.Pagination.Abstractions) |
Plain netstandard2.0 request/response models (PagedRequest, PageRequestWithFilters, PagedResult<T>, DataFilter, DataSearchDefinition, DataOrderDefinition, DataPredefinedFilterDefinition, ...). Use this when you only need to share the contract between client and server. |
PagedListResult.Common -> (RzR.ResultMessage.Pagination.Core) |
The expression-builder layer on top of System.Linq.Expressions: dynamic search across text fields, ordering by property name, conditional filters, predefined records, validation. No EF Core dependency. |
PagedListResult -> (RzR.ResultMessage.Pagination.EntityFrameworkCore) |
The IQueryable<T> extensions that wire everything together for Microsoft.EntityFrameworkCore consumers: GetPaged, GetPagedAsync, GetPagedWithMainFiltersAsync, GetPagedWithFiltersAsync. |
PagedListResult.Web -> (RzR.ResultMessage.Pagination.AspNetCore) |
The ASP.NET Core integration: a base controller, a model binder ([FromPagedQuery]), the dual-host query wrappers PagedQuery<T> / PagedQueryWithFilters<T> (namespace ...AspNetCore.Query; bind in both MVC and minimal API), an action filter and an endpoint filter that emit the standard pagination headers, an allow-list registry, and an API Explorer convention. Targets netstandard2.1 and .NET 5/6/7/8/9 (the PagedQuery<T> wrappers are gated to .NET 7+). |
The numeric values for condition, orderDirection and parentFilterLinkType map to the FilterType, OrderDirection and FilterConditionType enums; the full mapping table is in the usage guide and the Data Models Reference wiki page.
{
"currentPage": 1,
"pageCount": 0,
"pageSize": 10,
"rowCount": 0,
"executionDetails": {
"executionTimeMs": 0,
"executionDate": "yyyy-MM-ddTHH:mm:ss.fff"
},
"response": [],
"isSuccess": true,
"messages": []
}The Web layer (RzR.ResultMessage.Pagination.AspNetCore) adds a number of conveniences for ASP.NET Core projects:
- A registration entry point
services.AddPagedListResultWeb(...)that exposes options such asMaxPageSize,DefaultPageSize, header toggles and a status-code mapper. - A per-entity allow-list registered via
ConfigurePageable<TEntity>(b => b.AllowSort(...).AllowFilter(...).AllowSearch(...)). Property names not in the allow-list are rejected with a400 ValidationProblembefore any reflection runs. - An MVC model binder activated by
[FromPagedQuery](and[FromPagedQuery(typeof(TEntity))]), so you can acceptPagedRequest/PageRequestWithFiltersstraight from the query string in addition to the body-bound usage you already had. - Dual-host query wrappers
PagedQuery<TEntity>andPagedQueryWithFilters<TEntity>(.NET 7+, namespace...AspNetCore.Query). In minimal API they self-bind via theBindAsyncpattern; in MVC they also bind when decorated with[FromPagedQuery]. Either way they exposeIsValid/Errors(surfaced on the instance viaIPagedQueryValidation) and share the exact same parsing semantics as the[FromPagedQuery]pipeline. - Standard pagination headers emitted by the action filter (MVC) and by the endpoint filter
WithPagedResult()(minimal API):X-Total-Count,X-Page-Count,X-Page-Size,X-Current-Page, the RFC 5988Linkheader (first,prev,next,last), and an opt-inServer-Timing: paged;dur=.... - An API Explorer convention (
AddPagedListResultApiExplorer) that auto-attaches[Produces("application/json")],[ProducesResponseType(typeof(PagedResult<T>), 200)]and[ProducesResponseType(typeof(ProblemDetails), 400)]to any action whose return type resolves toIPagedResult<T>(includingTask<>,ValueTask<>andActionResult<>wrappers). Existing user-declared response types are preserved. - RFC 9457
application/problem+jsonon the failure path throughAggregatedGenericResultMessage.Web. The base controller helpers were renamed toPagedOkResult<T>andPagedXmlResult<T>. The previousJsonResult<T>andXmlResult<T>were removed in6.0.0.7820. AnXmlResult(...)call site fails to compile, but aJsonResult(...)one keeps compiling and silently rebinds to the inheritedResultBaseApiController.JsonResult<T>, which returns the bare item array instead of the paged envelope. Seedocs/usage.mdsection 3.1.
The full walk-through with code samples lives in the usage guide.
{ "page": 1, "pageSize": 20, "search": { "search": "usb", "searchInAllTextFields": true, "searchInAllFields": false, "customSearchTextProperties": [ "Name", "Description" ] }, "order": { "orderByProperty": "Price", "orderDirection": 1, // 0 = Asc, 1 = Desc "orderByDefaultProperty": false }, "fields": [ "Id", "Name", "Price" ], "predefinedRecord": { "predefinedFieldName": "Id", "predefinedRecords": [ "1", "2", "3" ] }, "filters": [ { "filterValue": { "condition": 0, // Equals "propertyName": "Status", "values": [ "active" ], "compareValue": null }, "filterApplyOrder": 0, "dependencies": [] }, { "filterValue": { "condition": 14, // Between (uses values[0] as lower bound and compareValue as upper bound) "propertyName": "Price", "values": [ "10" ], "compareValue": "100" }, "filterApplyOrder": 1, "dependencies": [ { "parentFilterLinkType": 0, // 0 = And, 1 = Or "filterValue": { "condition": 18, // IsNotNull "propertyName": "PublishedOn", "values": [], "compareValue": null } } ] } ] }