Atualização eficiente
Processamento em lote
O EF Core ajuda a minimizar as viagens de ida e volta, reunindo automaticamente todas as atualizações em lote em uma única viagem de ida e volta. Considere o seguinte:
var blog = await context.Blogs.SingleAsync(b => b.Url == "http://someblog.microsoft.com");
blog.Url = "http://someotherblog.microsoft.com";
context.Add(new Blog { Url = "http://newblog1.microsoft.com" });
context.Add(new Blog { Url = "http://newblog2.microsoft.com" });
await context.SaveChangesAsync();
O acima carrega um blog do banco de dados, altera sua URL e, em seguida, adiciona dois novos blogs; para aplicar isso, duas instruções SQL INSERT e uma instrução UPDATE são enviadas para o banco de dados. Em vez de os enviar um a um, à medida que vão sendo adicionadas as instâncias do Blog, o EF Core rastreia essas alterações internamente e executa-as num único ciclo de ida e volta quando SaveChanges é chamado.
O número de instruções que o EF distribui em lotes numa única transação depende do provedor de banco de dados em uso. Por exemplo, a análise de desempenho mostrou que o processamento em lote é geralmente menos eficiente para o SQL Server quando menos de 4 instruções estão envolvidas. Da mesma forma, os benefícios do envio em lote diminuem após cerca de 40 instruções para o SQL Server, portanto, por padrão, o EF Core só executará até 42 instruções em um único lote e executará instruções adicionais em viagens de ida e volta separadas.
Os usuários também podem ajustar esses limites para alcançar um desempenho potencialmente mais alto - mas avalie cuidadosamente antes de modificá-los:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
o => o
.MinBatchSize(1)
.MaxBatchSize(100));
}
Use ExecuteUpdate e ExecuteDelete quando relevante
Vamos supor que você queira dar um aumento a todos os seus funcionários. Uma implementação típica para isso no EF Core seria semelhante à seguinte:
foreach (var employee in context.Employees)
{
employee.Salary += 1000;
}
await context.SaveChangesAsync();
Embora este seja um código perfeitamente válido, vamos analisar o que ele faz de uma perspetiva de desempenho:
- É realizada uma viagem de ida e volta à base de dados, para carregar todos os colaboradores relevantes; note que isso traz todos os dados da linha dos funcionários para o cliente, mesmo que apenas o salário seja necessário.
- O controle de alterações do EF Core cria instantâneos ao carregar as entidades e, em seguida, compara esses instantâneos com as instâncias para descobrir quais propriedades foram alteradas.
- Normalmente, uma segunda viagem de ida e volta do banco de dados é executada para salvar todas as alterações (observe que alguns provedores de banco de dados dividem as alterações em múltiplas viagens de ida e volta). Embora esse comportamento de envio em lote seja muito melhor do que fazer uma viagem de ida e volta para cada atualização, o EF Core ainda envia uma instrução UPDATE por funcionário, e o banco de dados deve executar cada instrução separadamente.
A partir do EF Core 7.0, você pode usar os métodos ExecuteUpdateAsync
e ExecuteDeleteAsync
para fazer a mesma coisa com muito mais eficiência:
await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));
Isso envia a seguinte instrução SQL para o banco de dados:
UPDATE [Employees] SET [Salary] = [Salary] + 1000;
Este UPDATE
realiza toda a operação em uma única viagem de ida e volta, sem carregar ou enviar dados reais para o banco de dados e sem fazer uso do maquinário de rastreamento de alterações da EF, o que impõe uma sobrecarga adicional. Para obter mais informações, consulte ExecuteUpdate
e ExecuteDelete
.
Se você estiver usando uma versão mais antiga do EF Core que ainda não oferece suporte a ExecuteUpdate
e ExecuteDelete
ou quiser executar uma instrução SQL complexa que não é suportada por esses métodos, ainda poderá usar uma consulta SQL para executar a operação:
context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");
Para saber mais sobre as diferenças entre SaveChanges
e ExecuteUpdate
/ExecuteDelete
, consulte a página Visão geral do sobre como salvar dados.