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.