Skip to content

Getting Started

RzR edited this page Jul 4, 2026 · 3 revisions

Getting Started

This page walks you from dotnet add package to a working paged endpoint in a few minutes. The shortest path is the EF Core extensions; the optional second half wires the same flow into ASP.NET Core (controllers and minimal APIs).

1. Install

For most projects you only need the top-level package, which transitively brings in the data models and the expression builders:

Install-Package RzR.ResultMessage.Pagination.Abstractions

Install-Package RzR.ResultMessage.Pagination.Core

Install-Package RzR.ResultMessage.Pagination.EntityFrameworkCore

Install-Package RzR.ResultMessage.Pagination.AspNetCore

If you also want the ASP.NET Core integration (controllers, model binders, minimal-API helpers, headers, OpenAPI conventions), add the Web integration package as well. Its assembly is RzR.ResultMessage.Pagination.AspNetCore and it requires the matching framework target.

You only need... Install
The DTO contract shared with a client RzR.ResultMessage.Pagination.Abstractions
The IQueryable<T> extensions on top of EF Core RzR.ResultMessage.Pagination.EntityFrameworkCore
Everything plus the ASP.NET Core glue RzR.ResultMessage.Pagination.EntityFrameworkCore and RzR.ResultMessage.Pagination.AspNetCore

2. Page an IQueryable<T> (no web framework involved)

Open a using directive on RzR.ResultMessage.Pagination.EntityFrameworkCore and call GetPagedAsync on any IQueryable<T>:

using RzR.ResultMessage.Pagination.EntityFrameworkCore;
using RzR.ResultMessage.Pagination.Abstractions.Models.Result;

PagedResult<Product> page = await _db.Products
    .OrderBy(p => p.Id)
    .GetPagedAsync(page: 1, pageSize: 25, cancellationToken);

// page.Response       -> IList<Product>
// page.RowCount       -> total filtered rows
// page.PageCount      -> total pages
// page.CurrentPage    -> 1
// page.PageSize       -> 25
// page.HasNextPage    -> derived
// page.HasPreviousPage-> derived
// page.ExecutionDetails.ExecutionTimeMs

That is the minimum surface of the library. Everything else builds on top of this primitive.

3. Accept a PagedRequest from the client

The same call signature accepts a PagedRequest (or its PageRequestWithFilters subclass) coming straight from the API consumer. With this overload you also get search, ordering, projection and predefined-records handling, all driven by the request body:

public async Task<PagedResult<Product>> GetProducts(
    PagedRequest request, CancellationToken ct)
{
    var query = _db.Products.AsNoTracking();

    return await query.GetPagedAsync(request, cancellationToken: ct);
}

If the request type extends PageRequestWithFilters, switch to one of the filter-aware overloads:

return await query.GetPagedWithFiltersAsync(
    request,
    new DefaultPrimaryKeyDefinition("Id"), // optional, only needed for predefined records
    filterLink: FilterConditionType.And,
    cancellationToken: ct);

For the field-by-field semantics of PagedRequest, see Data Models Reference. For the difference between the three filter variants, see EntityFramework Core Extensions.

4. Wire it into ASP.NET Core (MVC)

Inherit from BaseApiPagedResultController to get the success and failure helpers:

using Microsoft.AspNetCore.Mvc;
using RzR.ResultMessage.Pagination.AspNetCore;
using RzR.ResultMessage.Pagination.Abstractions.Abstractions;

[Produces("application/json")]
[Route("api/[controller]/[action]")]
public sealed class ProductsController : BaseApiPagedResultController
{
    private readonly AppDbContext _db;
    public ProductsController(AppDbContext db) => _db = db;

    [HttpPost]
    public async Task<IActionResult> List(
        [FromBody] PageRequestWithFilters request, CancellationToken ct)
    {
        IPagedResult<Product> result = await _db.Products
            .AsNoTracking()
            .GetPagedWithFiltersAsync(request, cancellationToken: ct);

        return PagedOkResult(result);
    }
}

PagedOkResult returns 200 OK with the full envelope on success and an RFC 9457 application/problem+json body on failure. See MVC Integration for the [FromPagedQuery] query-string variant and the obsolete-helper notes.

In Program.cs register the integration once:

builder.Services
    .AddControllers()
    .AddPagedListResultApiExplorer();      // OpenAPI auto-annotations

builder.Services
    .AddPagedListResultWeb(o =>
    {
        o.MaxPageSize = 100;
        o.DefaultPageSize = 25;
    });

That single call wires the model binder, the response-header filter, the per-type metadata registry and the MvcOptions convention. See Configuration and Options for the complete option reference.

5. Minimal API endpoint (.NET 7 and newer)

The minimal-API surface mirrors the MVC pipeline through dedicated bindable wrappers. PagedQuery<T> and PagedQueryWithFilters<T> live in RzR.ResultMessage.Pagination.AspNetCore.Query (renamed from ...MinimalApi in v5) and, as of v5, also bind in MVC when used as [FromPagedQuery] action parameters — see MVC Integration.

app.MapGet("/products",
    async (PagedQueryWithFilters<Product> q, AppDbContext db, HttpContext ctx, CancellationToken ct) =>
    {
        if (!q.IsValid)
            return Results.ValidationProblem(
                q.Errors.ToDictionary(e => e.Key, e => new[] { e.Value }));

        var paged = await db.Products
            .AsNoTracking()
            .GetPagedWithFiltersAsync(q, cancellationToken: ct);

        return paged.ToPagedHttpResult(ctx);
    })
    .WithPagedResult();

WithPagedResult() attaches the endpoint filter that emits the standard pagination headers, and ToPagedHttpResult makes sure the full paged envelope (paging metadata included) is serialized on success while still routing failures through the upstream ProblemDetails machinery. Details in Minimal API Integration.

6. Test it

Once your endpoint is up, the supported request shapes are:

  • Body-bound JSON (any of the request models). See Data Models Reference.

  • Query string. See Query String Syntax for the full grammar:

    GET /products
        ?page=2
        &pageSize=20
        &order=price:desc
        &search=usb
        &filter=status:Equals:active
        &filter=price:Between:10,100
    

The response always carries the PagedResult<T> envelope (paging metadata + Response items + execution details + result messages). On failure, the body is an RFC 9457 ProblemDetails document.

Where to go next

Clone this wiki locally