Quando Dapper é uma boa escolha

Use Dapper quando SQL claro e explícito se encaixa no acesso a dados, como consultas de leitura críticas, relatórios ou operações em que a equipe quer controlar o SQL. Ele não é automaticamente mais rápido em toda carga e não oferece por si só change tracking, migrations ou uma unidade de trabalho. Escolha pela necessidade e pela familiaridade operacional do time.

Se o gargalo está no plano, no volume transferido ou em consultas N+1, trocar ORM sem investigar pode não ajudar. Compare planos e medições reais com o roteiro de performance no EF Core e SQL.

Parametrize todo valor externo

Parâmetros mantêm valores separados do texto SQL e ajudam a evitar injeção. Nunca concatene entrada de usuário para formar valores, cláusulas ou filtros. Nomes de tabela e coluna não podem ser protegidos como valores parametrizados; quando forem dinâmicos, escolha entre alternativas de uma lista fixa validada.

const string sql = """
    SELECT Id, CriadoEm, Status
    FROM dbo.Pedido
    WHERE Id = @Id;
    """;

await using var conexao =
    await _conexoes.AbrirAsync(cancellationToken);

var pedido = await conexao.QuerySingleOrDefaultAsync<PedidoResumo>(
    new CommandDefinition(
        sql,
        new { Id = id },
        cancellationToken: cancellationToken));

O exemplo supõe uma fábrica de conexão da aplicação e um tipo PedidoResumo. Passe parâmetros por objeto ou estrutura tipada, e não por interpolação de strings. Ajuste sintaxe e recursos SQL à versão de SQL Server suportada.

Considere resultados e cardinalidade

Escolha o método de leitura pelo contrato: resultado único, opcional, sequência ou múltiplos conjuntos. Se um registro deveria ser único, prefira uma operação que detecta linhas inesperadas em vez de aceitar silenciosamente a primeira. Projete apenas as colunas necessárias e garanta que o tipo mapeado corresponde a nulabilidade, tipos e nomes retornados pelo SQL.

Para coleções, pagine com ordenação estável e limite máximo. Dapper expande coleções em cláusulas IN, mas o tamanho e os limites de parâmetros do SQL Server ainda importam; para grandes lotes, avalie table-valued parameters ou estratégia de carga apropriada.

Transações e ciclo de vida de conexões

Uma operação que exige atomicidade precisa executar seus comandos na mesma conexão e transação. Passe a transação explicitamente a cada comando e defina escopo curto; não faça chamadas remotas enquanto locks permanecem retidos. Gerencie abertura e descarte com using/await using e um pool apropriado, sem compartilhar uma conexão aberta entre requisições concorrentes.

Gerencie mudanças de esquema com migrations ou scripts versionados, revisados e implantados de forma compatível. Dapper não mantém o modelo do banco sincronizado automaticamente com as classes C#.

Performance precisa ser medida

Compare consultas equivalentes, conjuntos representativos, pool de conexões, concorrência e percentis de latência. Inspecione plano real, leituras lógicas, bloqueios e transferência de dados. Uma consulta Dapper ainda pode fazer scan, retornar dados demais ou sofrer contenção. Não coloque consultas em cache sem considerar invalidação e consistência; veja também decisões de banco com SQL Server e cache-aside com Redis.

Documentação e leituras relacionadas