Skip to content

Usages

Usages

This section demonstrates practical usage patterns of the Flecto library in real applications. From simple filter binding to executing queries and returning paginated results, this guide covers typical end-to-end flows.


1. Full Request-Response Flow

This is the most common scenario in web APIs. A client sends a request, filters are validated, and a dynamic SQL query is built using Flecto and executed via Dapper.

flowchart LR
    B --> C
    F --> G


    subgraph -IN- Presentaion Layer
        A[Client Request #40;POST /search#41;] --> B[Validation: e.g. via FluentValidation]
    end

    subgraph Repo Layer
        C[FlectoBuilder] --> D(Select →.Search → .BindX → .ApplyPaging → .Build#40;#41;)
        C --> E(Clone#40;#41;.SelectCount#40;#41;.Build#40;#41;)
        E --> F[SearchResult<T>]
        D --> F

    end

    subgraph -OUT- Presentaion Layer
        G[Client Response with pagination]
    end

Example:

// Request.cs

public class Request
{
    public SearchFilter? Search { get; set; }
    public StringFilter? Name { get; set; }
    public NumericFilter<decimal>? Salary { get; set; }
    public PaginationFilter? Paging { get; set; }
}

// Response.cs

public class Response
{
    public SearchResult<Employee[]>? Data { get; set; }
}

// Controller.cs
//...
[HttpPost("PostSearch")]
public async Task<ActionResult<Response>> PostSearch(
    Request r, 
    CancellationToken token)
{
    var result = await _svc.Search(r, token);
    return Ok(new Response { Data = result });
}
//...

// Validator.cs
// ... e.g. FluentValidation
// ...
RuleFor(x => x.BoolFilter)
    .Custom((filter, context) =>
    {
        var errors = GetBoolFilterValidationErrors(filter, requireAtLeastOne: false);

        foreach (var error in errors)
        {
            context.AddFailure(error.Field, error.Error);
        }
    });
// ...

// Repository.cs
// ...
// ...

public async Task<SearchResult<Employee[]>> Search(Request r, CancellationToken token)
{
    var builder = new FlectoBuilder(_employeeTable, DialectType.Postgres)
        .Search(r.Search, "first_name", "last_name", "middle_name", "notes")
        .BindNumeric(r.Id, "id")
        .BindString(r.Name, "first_name")
        .BindNumeric(r.Salary, "salary");

    var (sqlCount, parametersCount) = builder
        .Clone()
        .SelectCount()
        .Build();

    using var connection = await CreateConnectionAsync();

    var totalRecords = await connection.QueryFirstAsync<int>(sqlCount, parametersCount);

    var (sql, parameters) = builder
        .Select(_employeeAllColumn)
        .ApplyPaging(r.Paging)
        .Build();

    var employees = await connection.QueryAsync<Employee>(sql, parameters);

    return new SearchResult<Employee[]>(
            employees.ToArray(),
            SearchMetadata.From(totalRecords, r.Paging));
}

2. Service-Layer Query Flow

flowchart LR
    A --> B
    C --> D


    subgraph Service Layer
       A[Create Filters]
    end

    subgraph Repo Layer
        B[FlectoBuilder] --> C(Select →.Search → .BindX → .ApplyPaging → .Build#40;#41;)     
    end

    subgraph OUT 
       D[Result]
    end

Example:

// Service.cs

// ...

public async Task<Employee> GetEmployee(long id, CancellationToken token)
{
    var f = new NumericFilter<long> { Eq = id };
    result = await _repo.GetEmployee(f, token);
}

// ...

// Repository.cs

// ...

public async Task<Employee> GetEmployee(NumericFilter<long> f, CancellationToken token)
{
    var builder = new FlectoBuilder(_employeeTable, DialectType.Postgres)
        .Select(_employeeAllColumn)
        .BindNumeric(r.Id, "id")

    using var connection = await CreateConnectionAsync();
    var employees = await connection.QueryAsync<Employee>(sql, parameters);

    return employees.FirstOrDefault();
}

// ...