Przejdź do treści

TailoredApps.Shared.Querying

NuGet License: MIT


Opis

Biblioteka dostarcza zestaw bazowych klas i interfejsów do budowania stronicowanych i sortowanych zapytań w aplikacjach .NET. Standaryzuje strukturę zapytań listowych w całej aplikacji — zamiast przekazywać page, pageSize, sortField jako oddzielne parametry w każdym miejscu, masz jeden spójny kontrakt.

Kluczowe typy: - QueryBase — abstrakcyjna klasa bazowa dla obiektów filtru zapytań - PagedAndSortedQuery<TQuery> — klasa bazowa łącząca filtrowanie, paginację i sortowanie - IPagedResult<T> — kontrakt wynikowy ze stronicowaniem: kolekcja + łączna liczba - SortDirection — enum Asc/Desc/Undefined


Instalacja

dotnet add package TailoredApps.Shared.Querying

Przykład użycia

Definicja filtru i zapytania

using TailoredApps.Shared.Querying;

// 1. Filter — konkretne kryteria wyszukiwania
public class CustomerFilter : QueryBase
{
    public string NameContains { get; set; }
    public string Email { get; set; }
    public bool? IsActive { get; set; }
    public DateTime? RegisteredAfter { get; set; }
}

// 2. Zapytanie stronicowane + sortowane
public class GetCustomersQuery : PagedAndSortedQuery<CustomerFilter>
{
    // Wszystkie parametry są odziedziczone:
    // Page, Count, SortField, SortDir, Filter, IsPagingSpecified, IsSortingSpecified
}

// 3. Wynik stronicowany
public class CustomerListResult : IPagedResult<CustomerDto>
{
    public ICollection<CustomerDto> Results { get; set; }
    public int Count { get; set; }  // łączna liczba (bez stronicowania)
}

Implementacja w repozytorium / serwisie

public async Task<CustomerListResult> GetCustomersAsync(
    GetCustomersQuery query,
    CancellationToken ct = default)
{
    var dbQuery = _context.Customers.AsQueryable();

    // Filtry
    if (!string.IsNullOrWhiteSpace(query.Filter?.NameContains))
        dbQuery = dbQuery.Where(c => c.Name.Contains(query.Filter.NameContains));

    if (!string.IsNullOrWhiteSpace(query.Filter?.Email))
        dbQuery = dbQuery.Where(c => c.Email == query.Filter.Email);

    if (query.Filter?.IsActive.HasValue == true)
        dbQuery = dbQuery.Where(c => c.IsActive == query.Filter.IsActive.Value);

    // Sortowanie
    if (query.IsSortingSpecified)
    {
        // Przykład z IsSortBy()
        if (query.IsSortBy("Name"))
            dbQuery = query.SortDir == SortDirection.Asc
                ? dbQuery.OrderBy(c => c.Name)
                : dbQuery.OrderByDescending(c => c.Name);
        else if (query.IsSortBy("RegisteredAt"))
            dbQuery = query.SortDir == SortDirection.Asc
                ? dbQuery.OrderBy(c => c.RegisteredAt)
                : dbQuery.OrderByDescending(c => c.RegisteredAt);
    }

    var totalCount = await dbQuery.CountAsync(ct);

    // Paginacja
    if (query.IsPagingSpecified)
    {
        var skip = (query.Page!.Value - 1) * query.Count!.Value;
        dbQuery = dbQuery.Skip(skip).Take(query.Count.Value);
    }

    var items = await dbQuery
        .Select(c => new CustomerDto { Id = c.Id, Name = c.Name, Email = c.Email })
        .ToListAsync(ct);

    return new CustomerListResult
    {
        Results = items,
        Count = totalCount
    };
}

API Reference

Typ Rodzaj Opis
QueryBase Klasa abstrakcyjna Klasa bazowa dla wszystkich obiektów filtrów zapytań
PagedAndSortedQuery<TQuery> Klasa abstrakcyjna Bazowe zapytanie: paginacja + sortowanie + filtr
IPagedAndSortedQuery<TQuery> Interfejs Kontrakt PagedAndSortedQuery
IPagedResult<T> Interfejs Wynik stronicowany: Results + Count
SortDirection Enum Undefined = 0, Asc = 1, Desc = 2
IPagingParameters Interfejs Page, Count, IsPagingSpecified
ISortingParameters Interfejs SortField, SortDir, IsSortingSpecified
IQueryParameters Interfejs Łączy IPagingParameters + ISortingParameters
IQuery<T> Interfejs Zapytanie z obiektem filtru: Filter
IPagedAndSortedRequest<TResponse, TQuery, TModel> Interfejs Kontrakt MediatR request z paginacją (używany przez MediatR.PagedRequest)
IsSortBy(string) Metoda Case-insensitive porównanie z SortField

🤖 AI Agent Prompt

## TailoredApps.Shared.Querying — Instrukcja dla agenta AI

Używasz TailoredApps.Shared.Querying do standaryzacji stronicowanych zapytań.

### Definicja typów
```csharp
// Filter dziedziczy QueryBase
public class MyFilter : QueryBase { public string Name { get; set; } }

// Zapytanie dziedziczy PagedAndSortedQuery<TFilter>
public class GetItemsQuery : PagedAndSortedQuery<MyFilter> { }

// Wynik implementuje IPagedResult<TItem>
public class ItemsResult : IPagedResult<ItemDto>
{
    public ICollection<ItemDto> Results { get; set; }
    public int Count { get; set; }
}

Parametry URL → automatyczny binding w ASP.NET Core

?page=1&count=20&sortField=Name&sortDir=Asc&filter.name=test

W serwisie/repozytorium

if (query.IsPagingSpecified)
    dbQuery = dbQuery.Skip((query.Page!.Value - 1) * query.Count!.Value).Take(query.Count.Value);

if (query.IsSortingSpecified)
{
    if (query.IsSortBy("Name"))
        dbQuery = query.SortDir == SortDirection.Asc ? dbQuery.OrderBy(x => x.Name) : dbQuery.OrderByDescending(x => x.Name);
}

Zasady

  • Count w IPagedResult = łączna liczba bez stronicowania (do kalkulacji stron w UI)
  • SortDirection.Undefined = brak sortowania (domyślne)
  • IsSortBy() sprawdza case-insensitive — używaj zawsze zamiast string.Equals ręcznie
  • IsPagingSpecified = OBIE wartości Page i Count muszą być != null ```