Salvar Dados

Enquanto a consulta permite que você leia os dados do banco de dados, salvar os dados significa adicionar novas entidades ao banco de dados, remover entidades ou modificar as propriedades das entidades existentes de alguma forma. O Entity Framework Core (EF Core) dá suporte a duas abordagens básicas para salvar dados no banco de dados.

Abordagem 1: controle de alterações e SaveChanges

Em muitos cenários, seu programa precisa consultar alguns dados do banco de dados, executar alguma modificação neles e salvar essas modificações com suporte; isso às vezes é chamado de "unidade de trabalho". Por exemplo, vamos supor que você tenha um conjunto de blogs e gostaria de alterar a propriedade Url de um deles. No EF, isso normalmente é feito da seguinte forma:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Single(b => b.Url == "http://example.com");
    blog.Url = "http://example.com/blog";
    context.SaveChanges();
}

O código acima executa as etapas a seguir :

  1. Ele usa uma consulta LINQ regular para carregar uma entidade a partir do banco de dados (consulte Consulta de Dados). As consultas do EF são de controle por padrão, o que significa que o EF controla as entidades carregadas no seu controlador de alterações interno.
  2. A instância da entidade carregada é manipulada como de costume, atribuindo uma propriedade .NET. O EF não está envolvido nessa etapa.
  3. Finalmente, DbContext.SaveChanges() é chamado. Nesse ponto, o EF detecta automaticamente todas as alterações, comparando as entidades com um instantâneo do momento em que foram carregadas. Todas as alterações detectadas são mantidas no banco de dados; ao usar um banco de dados relacional, isso normalmente envolve enviar, por exemplo, um SQL UPDATE para atualizar as linhas relevantes.

Observe que o texto acima descreve uma operação de atualização típica para dados existentes, mas princípios semelhantes são válidos para as entidades de adição e remoção. Você interage com o controlador de alterações do EF chamando DbSet<TEntity>.Add e Remove, fazendo com que as alterações sejam controladas. Em seguida, a EF aplica todas as alterações controladas ao banco de dados quando SaveChanges() é chamado (por exemplo, via SQL INSERT e DELETE ao utilizar um banco de dados relacional).

SaveChanges() oferece as seguintes vantagens:

  • Você não precisa escrever um código para rastrear quais entidades e propriedades foram alteradas, o EF faz isso automaticamente para você e só atualiza essas propriedades no banco de dados, melhorando o desempenho. Imagine se suas entidades carregadas estiverem associadas a um componente da interface do usuário, permitindo que os usuários alterem todas as propriedades que desejarem; o EF elimina o fardo de descobrir quais entidades e propriedades foram realmente alteradas.
  • Salvar alterações no banco de dados às vezes pode ser complicado! Por exemplo, se você deve adicionar um blog e algumas postagens desse blog, talvez seja necessário buscar a chave gerada pelo banco de dados para o blog inserido antes de inserir as postagens (já que elas precisam se referir ao blog). O EF faz tudo isso para você, eliminando a complexidade.
  • O EF pode detectar problemas de simultaneidade, como quando uma linha do banco de dados foi modificada por outra pessoa entre sua consulta e SaveChanges(). Mais detalhes estão disponíveis em Conflitos de simultaneidade.
  • Em bancos de dados que dão suporte a isso, o SaveChanges() encapsula automaticamente várias alterações em uma transação, assegurando que seus dados permaneçam consistentes se ocorrer uma falha. Mais detalhes estão disponíveis em Transações.
  • SaveChanges() também agrupa várias alterações em muitos casos, reduzindo significativamente o número de idas e vindas ao banco de dados e melhorando consideravelmente o desempenho. Mais detalhes estão disponíveis em Atualizações eficientes.

Para obter mais informações e códigos de exemplo sobre o uso básico SaveChanges(), consulte SaveChanges Básico. Para obter mais informações sobre o controle de alterações do EF, consulte Visão geral do controle de alterações.

Abordagem 2: ExecuteUpdate e ExecuteDelete ("atualização em massa")

Observação

Esse recurso foi introduzido no EF Core 7.0.

Embora o controle de alterações e o SaveChanges() sejam uma maneira poderosa de salvar alterações, eles têm algumas desvantagens.

Primeiro, o SaveChanges() exige que você consulte e rastreie todas as entidades que serão modificadas ou excluídas. Se você precisar, por exemplo, excluir todos os blogs com uma classificação abaixo de um determinado limite, deverá consultar, materializar e rastrear um número potencialmente enorme de linhas e ter SaveChanges() para gerar uma instrução DELETE para cada uma delas. Os bancos de dados relacionais podem fornecer uma alternativa muito mais eficiente: um único comando DELETE pode ser enviado, especificando quais linhas devem ser excluídas através de uma cláusula WHERE, mas o modelo SaveChanges() não permite gerar isso.

Para dar suporte a esse cenário de "atualização em massa", você pode utilizar ExecuteDelete da seguinte forma:

context.Blogs.Where(b => b.Rating < 3).ExecuteDelete();

Isso permite que você expresse uma instrução SQL DELETE por meio de operadores LINQ regulares, semelhante a uma consulta LINQ regular, fazendo com que o SQL a seguir seja executado no banco de dados:

DELETE FROM [b]
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3

Isso é executado com muita eficiência no banco de dados, sem carregar nenhum dado do banco de dados ou envolver o controlador de alterações do EF. Da mesma forma, ExecuteUpdate permite que você expresse uma instrução SQL UPDATE.

Mesmo se não estiver alterando entidades em massa, você poderá saber exatamente quais propriedades de cada entidade deseja alterar. Usar a API de controle de alterações para realizar a alteração pode ser excessivamente complexo, exigindo a criação de uma instância de entidade, acompanhando-a através de Attach, fazendo suas alterações e, finalmente, chamando SaveChanges(). Para esses cenários, ExecuteUpdate e ExecuteDelete podem ser uma maneira consideravelmente mais simples de expressar a mesma operação.

Por fim, tanto o controle de alterações quanto o próprio SaveChanges() impõem uma certa sobrecarga de runtime. Se estiver escrevendo um aplicativo de alto desempenho, ExecuteUpdate e ExecuteDelete permitirão que você evite esses dois componentes e gere com eficiência a instrução que você deseja.

Observe, porém, que ExecuteUpdate e ExecuteDelete também têm certas limitações:

  • Esses métodos são executados imediatamente e, no momento, não podem ser agrupados com outras operações. Por outro lado, SaveChanges() pode agrupar várias operações em lotes.
  • Como o controle de alterações não está envolvido, é responsabilidade sua saber exatamente quais entidades e propriedades precisam ser alteradas. Isso pode significar um controle de alterações mais manual e de baixo código sobre o que precisa ser alterado e o que não precisa.
  • Além disso, como o controle de alterações não está envolvido, esses métodos não aplicam automaticamente o Controle de Simuntaneidade ao persistir as alterações. No entanto, você ainda pode adicionar explicitamente uma cláusula Where para implementar o controle de simultaneidade por conta própria.
  • No momento, somente a atualização e a exclusão têm suporte; a inserção deve ser feita por meio de DbSet<TEntity>.Add e SaveChanges().

Para obter mais informações e códigos de exemplo, consulte ExecuteUpdate e ExecuteDelete.

Resumo

A seguir, apresentamos algumas diretrizes sobre quando utilizar cada abordagem. Observe que essas não são regras absolutas, mas fornecem uma regra prática útil:

  • Se você não souber antecipadamente quais alterações ocorrerão, utilize SaveChanges; ele detectará automaticamente quais alterações precisam ser aplicadas. Exemplos de cenários:
    • "Quero carregar um Blog do banco de dados e exibir um formulário permitindo que o usuário o altere."
  • Se você precisar manipular um gráfico de objetos (ou seja, vários objetos interconectados), use SaveChanges; ele descobrirá a ordem correta das alterações e como vincular tudo.
    • "Desejo fazer a atualização de um blog, alterando algumas de suas postagens e excluindo outras pessoas"
  • Se você deseja alterar um número potencialmente grande de entidades com base em algum critério, utilize ExecuteUpdate e ExecuteDelete. Exemplos de cenários:
    • "Desejo dar um aumento a todos os funcionários"
    • "Desejo excluir todos os blogs cujo nome começa com X"
  • Se você já sabe exatamente quais entidades deseja modificar e como alterá-las, utilize ExecuteUpdate e ExecuteDelete. Exemplos de cenários:
    • "Desejo excluir o blog cujo nome é 'Foo'"
    • "Desejo alterar o nome do blog com a ID 5 para 'Barra'"