Regira-Packages

Web Endpoints

Expose entity CRUD operations as HTTP endpoints using controllers:

Package Description
Regira.Entities.Web MVC attribute model via EntityControllerBase

Controllers

Controllers provide a more traditional, attribute-based approach using EntityControllerBase. Use this when you need full customisation, a per-entity pipeline with DTO mapping, or advanced sorting and includes.

Controller Selection

// basic (not recommended)
EntityControllerBase<TEntity>
EntityControllerBase<TEntity, TKey>
// basic (using DTOs, recommended)
EntityControllerBase<TEntity, TDto, TInputDto>
EntityControllerBase<TEntity, TSearchObject, TDto, TInputDto>
EntityControllerBase<TEntity, TKey, TSearchObject, TDto, TInputDto>
// complex (advanced operations)
EntityControllerBase<TEntity, TSearchObject, TSortBy, TIncludes, TDto, TInputDto>
EntityControllerBase<TEntity, TKey, TSearchObject, TSortBy, TIncludes, TDto, TInputDto>

Route prefix

Best practice: Keep controller [Route] attributes resource-relative[Route("[controller]")], or the resource name (e.g. [Route("products")]). Apply a shared api base once, in a single configurable place:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;

public sealed class RoutePrefixConvention(string prefix) : IApplicationModelConvention
{
    private readonly AttributeRouteModel _prefix = new(new RouteAttribute(prefix));
    public void Apply(ApplicationModel application)
    {
        foreach (var controller in application.Controllers)
            foreach (var selector in controller.Selectors)
                selector.AttributeRouteModel = selector.AttributeRouteModel is { } existing
                    ? AttributeRouteModel.CombineAttributeRouteModel(_prefix, existing)
                    : _prefix;
    }
}

// Program.cs — register once; every controller is served under /api/...
builder.Services.AddControllers(options =>
    options.Conventions.Add(new RoutePrefixConvention("api")));

Standard Endpoints

Simple and complex controller bases expose different endpoint sets. Simple bases (no TSortBy/TIncludes) expose Details, List (GET), GET /search, Save/Create/Modify/Patch/Delete. Complex bases additionally expose POST /list and POST /search.

Fetch Endpoints

Details (all bases):

// GET /{entities}/{id} - Single entity
Details(id) -> DetailsResult

List (all bases):

// GET /{entities} - Basic List
List() -> ListResult

// GET /{entities}?q={search}&page=1&pageSize=10 - List
List(searchObject, pagingInfo) -> ListResult

// Complex bases only — typed ?includes= and ?sortBy= bind on complex bases; simple bases ignore them
// GET /{entities}?categoryId=1&includes=Category&sortBy=CreatedDesc&sortBy=Title
List(searchObject, pagingInfo, includes[], sortBy[]) -> ListResult

Search (all bases):

// GET /{entities}/search?q={keyword}&page=1 - List + Count combined
// SearchResult carries a total Count alongside the items — use it to drive paging.
Search(searchObject, pagingInfo) -> SearchResult

Complex POST endpoints — complex bases only:

// POST /{entities}/list (collection of SearchObjects in body)
List([FromBody] searchObject[], pagingInfo, includes[], sortBy[]) -> ListResult

// POST /{entities}/search (collection of SearchObjects in body)
Search([FromBody] searchObject[], pagingInfo, includes[], sortBy[]) -> SearchResult

The SearchObject items return queries that are inclusive (using Union).

Paging

List and Search endpoints accept optional page and pageSize query parameters. By default, when no pageSize is sent, the full set is returned. You can configure a default and/or maximum page size so endpoints page automatically:

// Global — applies to every entity controller
services.UseEntities<AppDbContext>(options =>
{
    options.UseDefaults();
    // make sure to put this after UseDefaults()
    options.DefaultPageSize = 50;   // used when the request omits pageSize
    options.MaxPageSize = 200;      // any larger requested pageSize is clamped to this
    // or
    options.SetPageSize(pageSize: 50, maxPageSize: 200);
});

// Per-entity override — fully replaces the global values for that entity
services.For<Product>(e => e.SetPageSize(defaultPageSize: 25, maxPageSize: 100));

// Opt out — this entity is never force-paged, even when a global default is set
services.For<AuditLog>(e => e.SetPageSize());

Save (Add/Modify/Patch)

// POST /{entities} - Create
Create(inputDto) -> SaveResult

// PUT /{entities}/{id} - Full update
Modify(id, inputDto) -> SaveResult

// PATCH /{entities}/{id} - Partial update (JSON Merge Patch, RFC 7386)
Patch(id, partialJson) -> SaveResult

// POST /{entities}/save - Upsert
Save(inputDto) -> SaveResult

PATCH behaviour:

DELETE Endpoint

// DELETE /{entities}/{id} - Delete
Delete(id) -> DeleteResult

Notes


Response Types

Both approaches return the same standardised result wrappers:

public record DetailsResult<TDto>
{
    public TDto Item { get; set; }
    public long? Duration { get; set; } // Execution time in ms
}

public record ListResult<TDto>
{
    public IList<TDto> Items { get; set; }
    public long? Duration { get; set; }
}

public record SearchResult<TDto>
{
    public IList<TDto> Items { get; set; }
    public long Count { get; set; } // Total count for pagination
    public long? Duration { get; set; }
}

public record SaveResult<TDto>
{
    public long? Duration { get; set; }
    public bool IsNew { get; set; }
    public int Affected { get; set; }
    public TDto Item { get; set; }
}

public record DeleteResult<TDto>
{
    public TDto Item { get; set; } // The deleted item
    public long? Duration { get; set; }
}

Overview

  1. Index — Overview of Regira Entities
  2. Entity Models — Creating and structuring entity models
  3. Services — Implementing entity services and repositories
  4. Mapping — Mapping Entities to and from DTOs
  5. Web Endpoints — Exposing entity operations as HTTP endpoints
  6. Normalizing — Data normalization techniques
  7. Attachments — Managing file attachments
  8. Built-in Features — Ready to use components
  9. Checklist — Step-by-step guide for common tasks
  10. Practical Examples — Complete implementation examples