Skip to content

Architecture and Packages

RzR edited this page Jul 4, 2026 · 2 revisions

Architecture and Packages

This page describes the layering of the libraries, the dependencies between them, and the responsibilities each one owns. It is the page to read when you are deciding which package to install or where to put your own extension.

Package layout

The repository ships four NuGet packages built from the src/ folder:

Project folder NuGet ID Assembly Root namespace Target frameworks
src/PagedListResult.DataModels RzR.ResultMessage.Pagination.Abstractions RzR.ResultMessage.Pagination.Abstractions RzR.ResultMessage.Pagination.Abstractions.* netstandard2.0
src/PagedListResult.Common RzR.ResultMessage.Pagination.Core RzR.ResultMessage.Pagination.Core RzR.ResultMessage.Pagination.Core.* netstandard2.0
src/PagedListResult RzR.ResultMessage.Pagination.EntityFrameworkCore RzR.ResultMessage.Pagination.EntityFrameworkCore RzR.ResultMessage.Pagination.EntityFrameworkCore.* netstandard2.0 (multi-target by EF Core consumer)
src/PagedListResult.Web RzR.ResultMessage.Pagination.AspNetCore RzR.ResultMessage.Pagination.AspNetCore RzR.ResultMessage.Pagination.AspNetCore.* netstandard2.1; net5.0; net6.0; net7.0; net8.0; net9.0

There is no hidden circular dependency between them; the graph is strictly bottom-up:

DataModels  <-  Common  <-  PagedListResult (EF Core)  <-  PagedListResult.Web (ASP.NET Core)

Project responsibilities

PagedListResult.DataModels

Pure DTOs. The package contains no logic at all, only:

  • Request models: PagedRequest, PageRequestWithFilters, DataFilter, DataFilterValue, DataFilterDependence, DataOrderDefinition, DataSearchDefinition, DataPredefinedFilterDefinition, DefaultPrimaryKeyDefinition.
  • Result models: PagedResult<T>, PagedExecDetailsResult.
  • Abstractions: IPagedResult<TSource> (extends IResult<IList<TSource>> from RzR.ResultMessage).
  • Enums: FilterType, FilterConditionType, OrderDirection.

The package is netstandard2.0, so it can be consumed by .NET Framework, Mono, Xamarin or any .NET 5+ target. Use it when you only need to share the contract with a client (Blazor WASM, Angular client generated from OpenAPI, JS/TS SPA with hand-written types, etc.) without dragging EF Core or ASP.NET Core along.

PagedListResult.Common

The expression-builder layer. Built on top of System.Linq.Expressions, it converts the DTOs above into Expression<Func<T, bool>> predicates and IOrderedQueryable<T> ordering. Highlights:

  • FilterSourceByTextExpression for free-text search across all text columns or a custom subset.
  • FilterSourceOrderExtensions for property-name based ordering with optional default-property fallback.
  • FilterSourceQueryExtensions for the filter pipeline (And/Or, dependencies).
  • FilterPredefinedTopRecordExtensions and PredefinedRecordsHelper for the "pin top X rows" feature.
  • Attributes:
    • [PaginationDefaultOrderProperty] — marks a property as the implicit order target when the request asks for default ordering.
    • [PaginationDefaultTopRecordPrimaryKey] — marks the property used to match predefined record identifiers when no explicit field is provided.
  • ReflectionTypeStorage and friends — small reflection caches used by the builders.

The package has no EF Core dependency. You can use it with plain LINQ-to-Objects, with IQueryable<T> from any provider, or as a low-level building block in your own custom adapter.

PagedListResult (the EF Core extensions)

This is the package that you most often install directly. It exposes IQueryable<T> extension methods through the static PaginatedQueryExtensions class:

Method Purpose
GetPaged(page, pageSize) Plain pagination.
GetPaged(request, defaultPrimaryKey?) Pagination + search + ordering + projection + predefined records.
GetPagedAsync(page, pageSize, ct) Async variant of the simple form.
GetPagedAsync(request, defaultPrimaryKey?, ct) Async variant with PagedRequest.
GetPagedWithMainFiltersAsync(request, defaultPrimaryKey?, ct) Adds top-level AND filtering from PageRequestWithFilters.Filters.
GetPagedWithFiltersAsync(request, defaultPrimaryKey?, filterLink?, ct) Full filter pipeline including dependencies.

It also includes the SOAP/XML adapters (ToSoapXmlPagedResult on both PagedResult<T> and IPagedResult<T>) used by the Web integration when an XML response is requested.

The package's only third-party runtime dependency is Microsoft.EntityFrameworkCore because of ToListAsync and the DbContext probing used by DefaultPrimaryKeyDefinition discovery. If you do not want EF Core, stay on PagedListResult.Common and write the page slicing yourself.

PagedListResult.Web

The ASP.NET Core integration. It is structured around four service surfaces:

  1. Configuration (AddPagedListResultWeb, AddPagedListResultApiExplorer, WithPagedResult / AddPagedResultEndpointFilter).
  2. Bindings (the [FromPagedQuery] attribute, the PagedRequestModelBinderProvider, and the dual-host PagedQuery<T> / PagedQueryWithFilters<T> wrappers in RzR.ResultMessage.Pagination.AspNetCore.Query — they bind in MVC via [FromPagedQuery] and in Minimal API via BindAsync).
  3. Response shaping (PagedResultActionFilter, PagedResultEndpointFilter, PagedResponseHeaderWriter, PagedResultsHttpExtensions.ToPagedHttpResult, BaseApiPagedResultController).
  4. Discoverability (PagedResultApplicationModelConvention for OpenAPI/Swagger, the PageableMetadata allow-list and its registry).

Minimal-API features (PagedQuery<T>, PagedQueryWithFilters<T>, PagedResultEndpointFilter, WithPagedResult, ToPagedHttpResult) are guarded by #if NET7_0_OR_GREATER. Targeting netstandard2.1 or net5.0/net6.0 you still get the MVC binder, the action filter, the controller and the OpenAPI convention.

Folder layout inside PagedListResult.Web

Folder Contents
Abstractions/ IPageableMetadataRegistry, IPagedQueryValidation.
Attributes/ [FromPagedQuery] model-binding attribute.
Builders/ PagedListResultBuilder (returned by AddPagedListResultWeb) and PageableMetadataBuilder<T> (returned by ConfigurePageable<T>).
Configuration/ PagedListResultServiceCollectionExtensions, PagedListResultMvcBuilderExtensions, PagedResultEndpointRouteExtensions, PagedResultApplicationModelConvention.
Extensions/ PagedResultsHttpExtensions.ToPagedHttpResult (minimal-API success/failure adapter).
Filters/ PagedResultActionFilter (MVC) and PagedResultEndpointFilter (minimal API).
Helpers/ PagedQueryKeys, PagedRequestQueryParser, PagedResponseHeaderWriter, MinimalApiQueryBinder.
Query/ PagedQuery<TEntity>, PagedQueryWithFilters<TEntity> — dual-host: bind via BindAsync in Minimal API and via [FromPagedQuery] in MVC.
ModelBinding/ PagedRequestModelBinder, PagedRequestModelBinderProvider.
Models/ PageableMetadata, PagedListResultWebOptions, PagedAccessors, PagedParseResult.
Registries/ PageableMetadataRegistry (default singleton implementation).
BaseApiPagedResultController.cs Convenience base class for [ApiController] controllers.

External dependencies

The non-trivial third-party packages used at runtime are:

  • RzR.ResultMessage — provides Result<T> / IResult<T> and the message envelope, which PagedResult<T> extends.
  • RzR.ResultMessage.Web — provides ResultBaseApiController, the IProblemDetailsResultFactory and the minimal-API ResultToHttpResult.ToHttpResult adapter that ToPagedHttpResult builds upon.
  • Microsoft.EntityFrameworkCore — used by the EF Core extensions for ToListAsync and DbContext probing.

There are no analyzers, source generators, runtime IL emitters or proxies. All reflection is small, scoped and cached at static initialization.

Where to put your own extensions

A few rules of thumb:

  • New filter conditions or builders that do not depend on EF Core: extend PagedListResult.Common.
  • New IQueryable<T> methods specific to EF Core (e.g. IncludeMany, projection helpers): extend PagedListResult.
  • ASP.NET Core add-ons (custom action filters, alternative serializers, OpenAPI tweaks): extend PagedListResult.Web. Use the existing PagedListResultBuilder to chain registrations.
  • Brand-new request shapes that you only consume internally: derive from PagedRequest or PageRequestWithFilters instead of forking the model. The library is generic over TPageRequest : PagedRequest.

Clone this wiki locally