DEV Community

Cristiano Rodrigues for Unhacked

Posted on

Aumentando a performance do Dapper

Muitas vezes começamos a escrever códigos e deixamos passar detalhes que podem afetar a performance do nosso sistema. Acreditamos que o problema de performance é maior do que realmente é e, consequentemente, acabamos seguindo caminhos mais complicados.

Vamos considerar algo simples. Você está usando o Dapper, um Micro ORM, que é conhecido por sua rapidez em relação a outros ORMs. Mas será que você está usando o Dapper corretamente e aproveitando ao máximo essa performance?

Você costuma identificar cuidadosamente os parâmetros que passa para suas consultas?

Vamos criar uma classe Cliente e uma tabela para armazenar os clientes:

class Cliente
{
    public string Nome { get; set; }
    public int Id { get; set; }
    public string CPF { get; set; }
}
Enter fullscreen mode Exit fullscreen mode
CREATE TABLE CLIENTE (
    ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
    NOME VARCHAR(200) NOT NULL,
    CPF CHAR(11) NOT NULL
)
Enter fullscreen mode Exit fullscreen mode

Depois de criar a tabela, vamos inserir 1000 CPFs (falsos) em um loop e criar um índice para CPF:

CREATE UNIQUE NONCLUSTERED INDEX IX_CLIENTE_CPF   
    ON Cliente (Cpf)
    INCLUDE (Nome)
Enter fullscreen mode Exit fullscreen mode

Agora, vamos escrever um código simples para obter um cliente através do CPF, que é um código comum em muitas aplicações:

IConfigurationBuilder builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", false, true);
        IConfigurationRoot config = builder.Build();

        SqlConnection sqlConnection =
                new SqlConnection(
                    config.GetConnectionString("ecommerceConnectionString")
                    );
        sqlConnection.Open();

        var cliente = sqlConnection.QueryFirstOrDefault<Cliente>(
            "select * from Cliente where CPF = @cpf",
            new { cpf = "00000000090" });

        sqlConnection.Close();
Enter fullscreen mode Exit fullscreen mode

O que esperamos é que a query executada no banco de dados seja a mais performática possível uma vez que temos um índice pela coluna CPF. Vamos provar que o índice existe e que estamos com o melhor plano de execução possível.

Image description

A query foi executada realizando um Index Seek no índice por CPF que criamos anteriormente.

Agora vamos executar pela aplicação a "mesma" query.

Image description

Executando a query direto no SQL e obtendo o plano de execução.

Image description

O resultado não foi o esperado. Ao invés de executar um Index Seek, a query gerada pelo Dapper realizou um Index Scan. Isso ocorreu porque o Dapper interpretou o parâmetro da consulta como NVARCHAR por padrão, o que fez com que o SQL Server convertesse o parâmetro e alterasse o plano de execução, afetando a performance da consulta.

Para evitar esse problema e melhorar a performance da consulta, é necessário informar o tipo do parâmetro de string corretamente. No Dapper, podemos usar a classe DbString para especificar o tipo de parâmetro.

var cliente = sqlConnection.QueryFirstOrDefault<Cliente>(
            "select * from Cliente where CPF = @cpf",
            new
            {
                cpf = new DbString
                {
                    IsAnsi = true,
                    IsFixedLength = true,
                    Length = 11,
                    Value = "00000000090"
                }
            });
Enter fullscreen mode Exit fullscreen mode

Ao definir as propriedades de DbString, o Dapper gera uma consulta com o tipo de parâmetro correto, permitindo que o SQL Server faça um Index Seek. Note que é importante especificar a propriedade corretamente, de acordo com o tipo de dado da coluna no banco de dados.

Image description

Image description

O DbString é uma classe do Dapper que permite especificar o tipo exato de um parâmetro que será passado para o banco de dados. Assim o Dapper gera uma instrução que utiliza o tipo de dado correto para o parâmetro. Com isso, evitamos conversões desnecessárias e ajudamos a melhorar a performance das consultas.

O construtor do DbString permite definir várias propriedades, como IsAnsi, IsFixedLength, Length e Value. A propriedade IsAnsi indica se o tipo de dado é ANSI ou Unicode. A propriedade IsFixedLength indica se o tipo de dado é de comprimento fixo ou variável. A propriedade Length indica o tamanho máximo do campo, e a propriedade Value é o valor do parâmetro.

Parâmetros de acordo com o tipo:

Parametros

Esse pequeno detalhe muitas vezes passa despercebido durante as nossas implementações e pode afetar significativamente a performance de nossas aplicações, impactando diretamente na experiência dos usuários. Por isso, é importante dedicar tempo para otimizar o código, utilizando boas práticas e ferramentas que nos ajudem a identificar e corrigir possíveis problemas.

Até a próxima!

Top comments (2)

Collapse
 
fcatae profile image
Fabricio Catae

Show o artigo. Por acaso nao existe uma forma de configurar uma forma default? Porque isso facilitaria com menos codigo.

Collapse
 
cnr_br profile image
Cristiano Rodrigues

Fala meu camarada, obrigado por seu comentário. De fato, há uma outra forma de resolver esse problema. Você pode utilizar o seguinte trecho de código em C#:

Dapper.SqlMapper.AddTypeMap(typeof(string), System.Data.DbType.AnsiString);
Enter fullscreen mode Exit fullscreen mode

No entanto, vale ressaltar que essa solução pode não ser ideal caso existam colunas no seu banco de dados definidas como NVARCHAR. É importante levar isso em consideração ao escolher a melhor abordagem para resolver o problema em questão.