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; }
}
CREATE TABLE CLIENTE (
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
NOME VARCHAR(200) NOT NULL,
CPF CHAR(11) NOT NULL
)
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)
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();
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.
A query foi executada realizando um Index Seek no índice por CPF que criamos anteriormente.
Agora vamos executar pela aplicação a "mesma" query.
Executando a query direto no SQL e obtendo o plano de execução.
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"
}
});
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.
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:
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!