Como excluir um objeto por id com estrutura de entidade

105

Parece-me que tenho que recuperar um objeto antes de excluí-lo com a estrutura de entidade como abaixo

var customer = context.Customers.First(c => c.Id == 1);

context.DeleteObject(customer);

context.Savechanges();

Portanto, preciso acessar o banco de dados duas vezes. Existe uma maneira mais fácil?

Jeff
fonte
j.mp/f0x0Bh é a sua resposta. Esta é uma maneira legal e genérica de fazer isso
BritishDeveloper

Respostas:

93

No Entity Framework 6, a ação de exclusão é Remove. Aqui está um exemplo

Customer customer = new Customer () { Id = id };
context.Customers.Attach(customer);
context.Customers.Remove(customer);
context.SaveChanges();
dwkd
fonte
16
Porque Attach? Por que não apenas Removee SaveChanges?
runeks
3
Você deve anexar sua entidade no contexto porque se não fizer isso, receberá um erro durante a remoção. EF pode remover entidades apenas neste contexto
Pierre-Luc
3
@runeks de acordo com o manual, a entidade deve existir no contexto antes que a operação Remover possa ser realizada. Veja aqui docs.microsoft.com/en-us/dotnet/api/…
dwkd
1
não usei attach e
funcionou
58

O mesmo que @Nix, com uma pequena alteração para ser fortemente tipado:

Se você não quiser consultá-lo, basta criar uma entidade e excluí-la.

                Customer customer = new Customer () { Id = id };
                context.Customers.Attach(customer);
                context.Customers.DeleteObject(customer);
                context.SaveChanges();
Sawan
fonte
7
Não é perfeito, pois gera uma exceção se o objeto estiver ausente: "DbUpdateConcurrencyException: a instrução de atualização, inserção ou exclusão de armazenamento afetou um número inesperado de linhas (0)." Eu gostaria que ele ignorasse isso, como uma instrução DELETE faria.
Dunc
desculpe, isso causa validação que não é necessária e esperada sempre!
Hamed Zakery Miab
32

Pergunta semelhante aqui .

Com o Entity Framework, existe o EntityFramework-Plus (biblioteca de extensões).
Disponível no NuGet. Então você pode escrever algo como:

// DELETE all users which has been inactive for 2 years
ctx.Users.Where(x => x.LastLoginDate < DateTime.Now.AddYears(-2))
     .Delete();

Também é útil para exclusões em massa.

acarlon
fonte
36
É desafiador que isso não faça parte da biblioteca EF central agora.
nathanchere
1
@FerretallicA - concordou.
acarlon
2
este método é obsoleto, use: context.Users.Where (user => user.Id == id) .Delete ();
Manuel
Ele não funciona com o Azure SQL DataWarehouse devido ao erro "Uma cláusula FROM não tem suporte em uma instrução DELETE.". Mas o SQL bruto, como na resposta de Jonik, funciona.
Michael Freidgeim
1
O context.SaveChanges () é necessário?
Tomas Kubes
23

Se você não quiser consultá-lo, basta criar uma entidade e excluí-la.

Customer customer  = new Customer() {  Id = 1   } ; 
context.AttachTo("Customers", customer);
context.DeleteObject(customer);
context.Savechanges();
Nix
fonte
6

Estou usando o seguinte código em um dos meus projetos:

    using (var _context = new DBContext(new DbContextOptions<DBContext>()))
    {
        try
        {
            _context.MyItems.Remove(new MyItem() { MyItemId = id });
            await _context.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            if (!_context.MyItems.Any(i => i.MyItemId == id))
            {
                return NotFound();
            }
            else
            {
                throw ex;
            }
        }
    }

Dessa forma, ele consultará o banco de dados duas vezes apenas se ocorrer uma exceção ao tentar remover o item com o ID especificado. Então, se o item não for encontrado, ele retornará uma mensagem significativa; caso contrário, ele apenas devolve a exceção (você pode lidar com isso de uma maneira mais adequada ao seu caso usando diferentes blocos catch para diferentes tipos de exceção, adicionar mais verificações personalizadas usando blocos if etc.).

[Estou usando este código em um projeto MVC .Net Core / .Net Core com Entity Framework Core.]

demoníaca
fonte
2

A consulta SQL bruta é a maneira mais rápida, suponho

public void DeleteCustomer(int id)
{
   using (var context = new Context())
   {
      const string query = "DELETE FROM [dbo].[Customers] WHERE [id]={0}";
      var rows = context.Database.ExecuteSqlCommand(query,id);
      // rows >= 1 - count of deleted rows,
      // rows = 0 - nothing to delete.
   }
}
Jonik
fonte
19
Isso anula o propósito de usar a funcionalidade de objeto fortemente tipado em EF.
LawMan
4
Isso compromete o dinheiro da identidade EF. Depois disso, EF ainda retornará para você sua entidade excluída.
epóxi
1
Ele funciona com o Azure SQL DataWarehouse, quando outras soluções não.
Michael Freidgeim
1
Se você estiver fazendo isso, você também pode não usar um ORM. Eu imagino que isso comprometeria o cache EF.
Storm Muller,
Este estilo é vulnerável a ataques de injeção SQL. Neste exemplo específico, você está protegido porque a variável é um inteiro, mas nunca use esse padrão com uma variável de string.
thelem
2

A resposta de dwkd funcionou principalmente para mim no núcleo do Entity Framework, exceto quando vi esta exceção:

InvalidOperationException: A instância do tipo de entidade 'Cliente' não pode ser rastreada porque outra instância com o mesmo valor-chave para {'Id'} já está sendo rastreada. Ao anexar entidades existentes, certifique-se de que apenas uma instância de entidade com um determinado valor de chave seja anexada. Considere o uso de 'DbContextOptionsBuilder.EnableSensitiveDataLogging' para ver os valores de chave conflitantes.

Para evitar a exceção, atualizei o código:

Customer customer = context.Customers.Local.First(c => c.Id == id);
if (customer == null) {
    customer = new Customer () { Id = id };
    context.Customers.Attach(customer);
}
context.Customers.Remove(customer);
context.SaveChanges();
Jeffrey Rennie
fonte
1

Uma versão menor (quando comparada às anteriores):

var customer = context.Find(id);
context.Delete(customer);
context.SaveChanges();
Luis gouveia
fonte
Forneça algum contexto para este trecho de código e talvez alguma explicação sobre o que ele faz melhor do que as outras respostas deixadas na última década.
miken32 de
1

Na verdade, essa resposta foi tirada do curso de Scott Allen intitulado ASP.NET MVC 5 Fundamentals. Achei melhor compartilhar porque acho um pouco mais simples e intuitivo do que qualquer uma das respostas aqui. Observe também que, de acordo com Scott Allen e outros treinamentos que fiz, o método find é uma maneira otimizada de recuperar um recurso do banco de dados que pode usar o cache se já tiver sido recuperado. Neste código, coleção se refere a um DBSet de objetos. O objeto pode ser qualquer tipo de objeto genérico.

        var object = context.collection.Find(id);  
        context.collection.Remove(object);
        context.SaveChanges();
andy_coder
fonte