Trabalhando com estados de entidade

Este tópico abordará como adicionar e anexar entidades a um contexto e como o Entity Framework as processa durante SaveChanges. O Entity Framework cuida do acompanhamento do estado das entidades enquanto elas estão conectadas a um contexto, mas em cenários desconectados ou de N camadas, você pode informar ao EF em que estado suas entidades devem estar. As técnicas mostradas neste tópico se aplicam igualmente a modelos criados com o Code First e com o EF Designer.

Estados de entidade e SaveChanges

Uma entidade pode estar em cinco estados diferentes, conforme definido pela enumeração EntityState. Esses estados são:

  • Adicionado: a entidade está sendo acompanhada pelo contexto, mas ainda não existe no banco de dados
  • Inalterado: a entidade está sendo acompanhada pelo contexto e existe no banco de dados e seus valores de propriedade não diferem dos valores no banco de dados
  • Modificado: a entidade está sendo acompanhada pelo contexto e existe no banco de dados e alguns ou todos os seus valores de propriedade foram modificados
  • Excluído: a entidade está sendo acompanhada pelo contexto e existe no banco de dados, mas foi marcada para ser excluída do banco de dados na próxima vez que SaveChanges for chamado
  • Desanexado: a entidade não está sendo acompanhada pelo contexto

SaveChanges faz coisas diferentes para entidades em estados diferentes:

  • Entidades inalteradas não são tocadas por SaveChanges. As atualizações não são enviadas ao banco de dados para entidades no estado Inalterado.
  • As entidades adicionadas são inseridas no banco de dados e se tornam Inalteradas quando SaveChanges retorna.
  • As entidades modificadas são atualizadas no banco de dados e se tornam Inalteradas quando SaveChanges retorna.
  • As entidades excluídas são excluídas do banco de dados e desanexadas do contexto.

Os exemplos a seguir mostram maneiras pelas quais o estado de uma entidade ou de um grafo de entidade pode ser alterado.

Adicionar um novo tipo de entidade ao contexto

Uma nova entidade pode ser adicionada ao contexto chamando o método Add em DbSet. Isso coloca a entidade no estado Adicionado, o que significa que ela será inserida no banco de dados na próxima vez que SaveChanges for chamado. Por exemplo:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Blogs.Add(blog);
    context.SaveChanges();
}

Outra maneira de adicionar uma nova entidade ao contexto é alterar seu estado para Adicionado. Por exemplo:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Entry(blog).State = EntityState.Added;
    context.SaveChanges();
}

Por fim, você pode adicionar uma nova entidade ao contexto conectando-a a outra entidade que já está sendo acompanhada. Isso pode ser feito adicionando a nova entidade à propriedade de navegação de coleção de outra entidade ou definindo uma propriedade de navegação de referência de outra entidade para apontar para a nova entidade. Por exemplo:

using (var context = new BloggingContext())
{
    // Add a new User by setting a reference from a tracked Blog
    var blog = context.Blogs.Find(1);
    blog.Owner = new User { UserName = "johndoe1987" };

    // Add a new Post by adding to the collection of a tracked Blog
    blog.Posts.Add(new Post { Name = "How to Add Entities" });

    context.SaveChanges();
}

Observe que, para todos esses exemplos, se a entidade que está sendo adicionada tiver referências a outras entidades que ainda não foram acompanhadas, essas novas entidades também serão adicionadas ao contexto e serão inseridas no banco de dados na próxima vez em que SaveChanges for chamado.

Anexar uma entidade existente ao contexto

Se você tiver uma entidade que sabe que já existe no banco de dados, mas que não está sendo acompanhada no momento pelo contexto, poderá dizer ao contexto para acompanhar a entidade usando o método Attach no DbSet. A entidade estará no estado Inalterado no contexto. Por exemplo:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);

    // Do some more work...  

    context.SaveChanges();
}

Observe que nenhuma alteração será feita no banco de dados se SaveChanges for chamado sem fazer outra manipulação da entidade anexada. Isso ocorre porque a entidade está no estado Inalterado.

Outra maneira de anexar uma entidade existente ao contexto é alterar seu estado para Inalterado. Por exemplo:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

Observe que, para ambos os exemplos, se a entidade que está sendo anexada tiver referências a outras entidades que ainda não foram acompanhadas, essas novas entidades também serão anexadas ao contexto no estado Inalterado.

Anexar uma entidade existente, mas modificada, ao contexto

Se você tiver uma entidade que sabe que já existe no banco de dados, mas às quais alterações podem ter sido feitas, você poderá informar ao contexto para anexar a entidade e definir seu estado como Modificado. Por exemplo:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

Quando você alterar o estado para Modificado, todas as propriedades da entidade serão marcadas como modificadas e todos os valores de propriedade serão enviados ao banco de dados quando SaveChanges for chamado.

Observe que, se a entidade anexada tiver referências a outras entidades que ainda não foram acompanhadas, essas novas entidades serão anexadas ao contexto no estado Inalterado e não se tornarão Modificadas automaticamente. Se você tiver várias entidades que precisam ser marcadas como Modificadas, defina o estado para cada uma dessas entidades individualmente.

Alterar o estado de uma entidade acompanhada

Você pode alterar o estado de uma entidade que já está sendo acompanhada definindo a propriedade State em sua entrada. Por exemplo:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

Observe que chamar Add ou Attach para uma entidade que já está sendo acompanhada também pode ser usado para alterar o estado da entidade. Por exemplo, chamar Attach para uma entidade que está atualmente no estado Adicionado alterará seu estado para Inalterado.

Inserir ou atualizar padrão

Um padrão comum para alguns aplicativos é Adicionar uma entidade como nova (resultando em uma inserção de banco de dados) ou Anexar uma entidade como existente e marcá-la como modificada (resultando em uma atualização de banco de dados) dependendo do valor da chave primária. Por exemplo, ao usar chaves primárias inteiras geradas pelo banco de dados, é comum tratar uma entidade com uma chave zero como nova e uma entidade com uma chave diferente de zero como existente. Esse padrão pode ser alcançado definindo o estado da entidade com base em uma verificação do valor da chave primária. Por exemplo:

public void InsertOrUpdate(Blog blog)
{
    using (var context = new BloggingContext())
    {
        context.Entry(blog).State = blog.BlogId == 0 ?
                                   EntityState.Added :
                                   EntityState.Modified;

        context.SaveChanges();
    }
}

Observe que quando você alterar o estado para Modificado, todas as propriedades da entidade serão marcadas como modificadas e todos os valores de propriedade serão enviados ao banco de dados quando SaveChanges for chamado.