Pular para o conteúdo
.NETOrdenação dinâmica passo a passo
Módulo 05Consultas Avançadas

Ordenação dinâmica passo a passo

avancado 45 min de leitura·Atualizado · .NET 9
Resumo

Ordenação dinâmica permite ao cliente escolher os campos e a direção (orderBy=preco desc,nome asc). Implementamos com construção dinâmica de expressão, de forma segura contra injeção.

1. Objetivos de Aprendizagem#

  • Aceitar parâmetros de ordenação múltipla via query string.
  • Construir a cláusula ORDER BY dinamicamente e com segurança.
  • Definir uma ordenação padrão e validar campos permitidos.

2. Pré-requisitos#

  • Busca e filtragem (Módulos 17–18).

3. Conceito#

O cliente envia orderBy=preco desc,nome. Precisamos transformar isso numa ordenação aplicada à IQueryable. O desafio: fazer isso sem abrir brecha de injeção — só campos existentes e permitidos podem ser usados. Uma abordagem segura combina reflexão para validar propriedades e a biblioteca System.Linq.Dynamic.Core para aplicar.

4. Mão na Massa#

4.1. Setup#

Shell
dotnet add Catalog.Repository package System.Linq.Dynamic.Core

4.2. Implementação Passo a Passo#

  1. Helper que monta a string validada:
C#
using System.Reflection;
using System.Text;

public static class OrderQueryBuilder
{
    public static string CreateOrderQuery<T>(string? orderByQueryString)
    {
        var defaultOrder = $"{typeof(T).GetProperties()[0].Name} ascending";
        if (string.IsNullOrWhiteSpace(orderByQueryString)) return defaultOrder;

        var terms = orderByQueryString.Split(',', StringSplitOptions.RemoveEmptyEntries);
        var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        var sb = new StringBuilder();

        foreach (var term in terms)
        {
            var field = term.Trim().Split(' ')[0];
            var prop = props.FirstOrDefault(p =>
                p.Name.Equals(field, StringComparison.OrdinalIgnoreCase));
            if (prop is null) continue; // ignora campos inexistentes (segurança)

            var direction = term.Trim().EndsWith(" desc", StringComparison.OrdinalIgnoreCase)
                ? "descending" : "ascending";
            sb.Append($"{prop.Name} {direction}, ");
        }

        var result = sb.ToString().TrimEnd(',', ' ');
        return string.IsNullOrWhiteSpace(result) ? defaultOrder : result;
    }
}
  1. Aplicação no repositório:
C#
using System.Linq.Dynamic.Core;

var ordered = query.OrderBy(OrderQueryBuilder.CreateOrderQuery<Produto>(p.OrderBy));

4.3. Executando#

Shell
curl "http://localhost:5000/api/categorias/{catId}/produtos?orderBy=preco desc,nome"

5. Exemplo Completo#

Campos inexistentes ou não permitidos são ignorados, e sempre há uma ordenação padrão — nunca deixamos Skip/Take sem ordem definida (Módulo 30).

6. Boas Práticas e Armadilhas#

FaçaEvite
Validar campos contra as propriedades reais (allowlist)Interpolar o input direto no OrderBy dinâmico
Definir ordenação padrãoPaginar sem ORDER BY
Ignorar termos inválidos silenciosamente ou retornar 400Lançar exceção crua para input malformado
Atenção

System.Linq.Dynamic.Core executa strings como expressão. Nunca passe o input do usuário sem validar contra uma allowlist de propriedades — caso contrário, é uma brecha de injeção.

7. Segurança e Produção#

  • A allowlist por reflexão é a defesa central contra injeção de ordenação.
  • Índices nas colunas ordenáveis evitam sorts caros no banco.

8. Exercícios#

  • Fácil: adicione ordenação por data de criação.
  • Médio: retorne 400 quando nenhum campo válido for informado (em vez de usar o padrão).
  • Desafio: implemente a ordenação sem System.Linq.Dynamic.Core, montando Expression manualmente.

9. Resumo#

Ordenação dinâmica traduz orderBy=... em ORDER BY, validando campos por reflexão (allowlist) e garantindo ordenação padrão. Segurança contra injeção é obrigatória.

10. Próximos Passos#

Módulo 34: data shaping — deixar o cliente escolher quais campos retornar.

11. Referências#

33-01 — Ordenação dinâmica passo a passo | Curso ASP.NET Core