Estrutura de entidade. Excluir todas as linhas da tabela

280

Como posso remover rapidamente todas as linhas da tabela usando o Entity Framework?

Atualmente, estou usando:

var rows = from o in dataDb.Table
           select o;
foreach (var row in rows)
{
    dataDb.Table.Remove(row);
}
dataDb.SaveChanges();

No entanto, leva muito tempo para ser executado.

Existem alternativas?

Zhenia
fonte
22
Lendo as respostas, pergunto-me por que nenhum desses TRUNCATEadeptos se preocupa com restrições de chave estrangeira.
Gert Arnold #
2
Estou meio surpreso com o modo como as respostas aqui dão como certo que todo mundo está usando o Microsoft SQL Server, mesmo que o suporte a outros bancos de dados no Entity Framework remova tanto quanto eu possa encontrar informações sobre e certamente antecede essa pergunta por vários anos. . Dica: se uma resposta citar os nomes das tabelas nas instruções SQL com colchetes (como:) [TableName], ela não é portátil.
Mark Amery #

Respostas:

293

Para aqueles que pesquisam isso no Google e acabaram aqui como eu, é assim que você faz atualmente no EF5 e EF6:

context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");

Assumindo que o contexto é um System.Data.Entity.DbContext

Ron Sijm
fonte
20
Para sua informação, para usar TRUNCATE, o usuário deve ter a permissão ALTER na tabela. ( Stackoverflow.com/questions/4735038/... )
Alex
7
@ Alex Só perdi uma tonelada de tempo com o erro "Não é possível encontrar o objeto MyTable porque ele não existe ou você não tem permissões". por esse motivo exato - as permissões ALTER raramente são concedidas aos aplicativos EF, e a mensagem de erro realmente o envia a um ganso selvagem.
Chris Moschini
8
Eu tive problemas, pois minha mesa fazia parte de um relacionamento de chave estrangeira, mesmo que fosse a mesa principal nesse relacionamento. Acabei usando context.Database.ExecuteSqlCommand ("DELETE FROM [Interesses]"); em vez
Dan Csharpster
2
Observe que, embora as [fugas aqui sejam específicas para o SQL Server, o TRUNCATEcomando não é - faz parte do ANSI SQL e, portanto, funcionará na maioria dos dialetos SQL (embora não seja SQLite).
Mark Amery
207

Aviso: O seguinte é adequado apenas para tabelas pequenas (pense <1000 linhas)

Aqui está uma solução que usa a estrutura da entidade (não o SQL) para excluir as linhas, portanto, não é específico do SQL Engine (R / DBM).

Isso pressupõe que você esteja fazendo isso para testes ou situações semelhantes. Ou

  • A quantidade de dados é pequena ou
  • O desempenho não importa

Basta ligar para:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

Assumindo este contexto:

public class VotingContext : DbContext
{
    public DbSet<Vote> Votes{get;set;}
    public DbSet<Poll> Polls{get;set;}
    public DbSet<Voter> Voters{get;set;}
    public DbSet<Candidacy> Candidates{get;set;}
}

Para código mais organizado, você pode declarar o seguinte método de extensão:

public static class EntityExtensions
{
    public static void Clear<T>(this DbSet<T> dbSet) where T : class
    {
        dbSet.RemoveRange(dbSet);
    }
}

Então o acima se torna:

VotingContext.Votes.Clear();
VotingContext.Voters.Clear();
VotingContext.Candidacy.Clear();
VotingContext.Polls.Clear();
await VotingTestContext.SaveChangesAsync();

Recentemente, usei essa abordagem para limpar meu banco de dados de teste para cada execução do caso de teste (é obviamente mais rápido do que recriar o banco de dados a partir do zero a cada vez, embora eu não tenha verificado a forma dos comandos de exclusão que foram gerados).


Por que isso pode ser lento?

  1. A EF obterá TODAS as linhas (VotingContext.Votes)
  2. e, em seguida, usará seus IDs (não sei exatamente como, não importa), para excluí-los.

Portanto, se você estiver trabalhando com uma quantidade séria de dados, você matará o processo do servidor SQL (ele consumirá toda a memória) e a mesma coisa para o processo IIS, já que o EF armazenará todos os dados da mesma maneira que o servidor SQL. Não use este se sua tabela contiver uma quantidade séria de dados.

Ahmed Alejo
fonte
1
Ótima resposta, agilizei meu código de exclusão de todas as linhas por um fator de 10! Observe que eu tive que renomear o método de extensão estática Clear () para algo como ClearDbSet (), já que eu já tinha outro método de extensão estática Clear () definido em outra parte do meu projeto.
dodgy_coder
1
A renomeação do @dodgy_coder não é necessária pelo motivo que você deu, pois o método de extensão é para DbSet, IDbSet e não IEnumerable, IList, ICollection, ICache ou qualquer outra interface que "Clear" seja necessária. a preferência pelo método de extensão é o tipo no qual são definidos. mas se isso parecer mais claro para você e não parecer redundante, ótimo! Estou feliz por ajudar no desempenho! Felicidades!
Ahmed Alejo
Fico feliz que você declarou apenas para pequenas mesas. Espero que as pessoas tenham a importância da sua explicação. porque esse caminho é o açúcar sintático, o caminho correto é o que Ron Sijm sugeriu. porque você não carrega os dados antes de removê-los. aplaude, porém, por mostrar e explicar essa maneira de fazê-lo.
Yedevtxt 31/08/18
3
Isso não redefine a chave de identidade. Então, se você limpar 10 registros, o próximo ainda será 11.
MDave
89

O uso do TRUNCATE TABLEcomando SQL será o mais rápido, pois opera na tabela e não em linhas individuais.

dataDb.ExecuteStoreCommand("TRUNCATE TABLE [Table]");

Supondo que dataDbseja um DbContext(não um ObjectContext), você pode agrupá-lo e usar o método como este:

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
Rudi Visser
fonte
Se você receber um erro de permissões quando você tentar isso, basta mudar TRUNCATE TABLEparaDELETE FROM
codemonkey
1
@codeMonkey apenas tomar nota que estas são operações diferentes, mas terá (quase) o mesmo efeito líquido 👍
Rudi Visser
3
Mas DELETE não redefine a semente de IDENTITY. Isso pode ser problemático em determinadas situações.
Steve
43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();
user3328890
fonte
11
Isso não deve ser usado porque você executa uma seleção completa e uma exclusão depois, em vez de apenas uma exclusão. Do ponto de vista do desempenho do tempo, este é um grande NÃO!
HellBaby
2
@HellBaby A menos que seja raramente chamado e, portanto, o desempenho é bastante irrelevante.
13134 Alex
6
Mesmo que isso raramente seja chamado, isso é ruim. Uma tabela com apenas 3.000 entradas pode levar mais de 30 segundos devido à velocidade do rastreamento de alterações da EF.
Leve
1
Isso só é adequado para pequenas mesas (<1000 linhas)
Amir Touitou
Perfeito para o meu banco de dados In-memory em minha Unidade Testes :)
SimonGates
38
using (var context = new DataDb())
{
     var ctx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext;
     ctx.ExecuteStoreCommand("DELETE FROM [TableName] WHERE Name= {0}", Name);
}

ou

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
Manish Mishra
fonte
1
mas quando eu escrevo. "query.Delete ();" - "Excluir" não reconhecido
Zhenia 05/03
1
adicionar referência de System.Data.Entity e EntityFramework em ur projeto atual
Manish Mishra
Qual método de extensão é Excluir?
Ahmed Alejo
Tanto quanto eu sei que não é um método de extensão Deleteem IQueryable- Eu estou supondo Manish estava usando algo como EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
nula
Eu editei minha resposta, antes era enganosa. @ nulo, você está certo, essa .Deleteera uma extensão personalizada e, no calor de postar a resposta primeiro, esqueci totalmente de mencionar a definição desse costume .Delete. :)
Manish Mishra
23

Você pode fazer isso sem o Foreach

dataDB.Table.RemoveRange(dataDB.Table);
dataDB.SaveChanges();

Isso removerá todas as linhas

Omid Farvid
fonte
Truncará itens de propriedades de navegação?
John Deer
19

Isso evita o uso de qualquer sql

using (var context = new MyDbContext())
{
    var itemsToDelete = context.Set<MyTable>();
    context.MyTables.RemoveRange(itemsToDelete);
    context.SaveChanges();
}
Rob Sedgwick
fonte
9

Me deparei com essa pergunta quando tive que lidar com um caso específico: atualização completa do conteúdo em uma tabela "folha" (sem FKs apontando para ele). Isso envolveu a remoção de todas as linhas e a colocação de novas informações sobre as linhas, e isso deve ser feito de maneira transacional (não quero terminar com uma tabela vazia, se as inserções falharem por qualquer motivo).

Eu tentei a public static void Clear<T>(this DbSet<T> dbSet)abordagem, mas novas linhas não são inseridas. Outra desvantagem é que todo o processo é lento, pois as linhas são excluídas uma a uma.

Então, mudei para TRUNCATEabordagem, pois é muito mais rápido e também é ROLLBACKable . Também redefine a identidade.

Exemplo usando padrão de repositório:

public class Repository<T> : IRepository<T> where T : class, new()
{
    private readonly IEfDbContext _context;

    public void BulkInsert(IEnumerable<T> entities)
    {
        _context.BulkInsert(entities);
    }

    public void Truncate()
    {
        _context.Database.ExecuteSqlCommand($"TRUNCATE TABLE {typeof(T).Name}");
    }
 }

 // usage 
 DataAccess.TheRepository.Truncate();
 var toAddBulk = new List<EnvironmentXImportingSystem>();

 // fill toAddBulk from source system
 // ...

 DataAccess.TheRepository.BulkInsert(toAddBulk);
 DataAccess.SaveChanges();

Obviamente, como já mencionado, esta solução não pode ser usada por tabelas referenciadas por chaves estrangeiras (TRUNCATE falha).

Alexei
fonte
Dois comentários: 1. O nome da tabela deve ser colocado em [...]. Uma das minhas classes / tabelas é chamada "Transação", que é uma palavra-chave SQL. 2. Se o objetivo é limpar todas as tabelas em um banco de dados para teste de unidade, os problemas com restrições de chave estrangeira podem ser facilmente contornados ordenando que as tabelas as processem de uma maneira que as tabelas filho sejam truncadas antes das tabelas pai.
Christoph
@Christoph - 1. Sim, isso é verdade. Eu perdi isso, porque sempre nomeio tabelas para evitar palavras-chave, pois isso pode causar problemas. 2. Se bem me lembro, as tabelas referenciadas pelos FKs não podem ser truncadas (lançamentos do SQL Server Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), mesmo que estejam vazias, portanto, os FKs devem ser descartados e recriados para serem usados ​​de TRUNCATEqualquer maneira.
Alexei
5

E se

      using(var db = new MyDbContext())
            {
               await db.Database.ExecuteSqlCommandAsync(@"TRUNCATE TABLE MyTable"););
            }

causas

Não é possível truncar a tabela 'MyTable' porque está sendo referenciada por uma restrição FOREIGN KEY.

Eu uso isso :

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }
Zakos
fonte
1
Se você tiver restrições de chave estrangeira: (1) SQL pode ser simplificado para "DELETE FROM MyTable". (2) Isso não redefinirá o contador de ID se você o tiver definido como incremento automático (truncado).
RGH
5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();
DBB
fonte
4

Se você deseja limpar todo o banco de dados.

Devido às restrições de chave estrangeira, é importante qual sequência as tabelas estão truncadas. Essa é uma maneira de forçar brutais essa sequência.

    public static void ClearDatabase<T>() where T : DbContext, new()
    {
        using (var context = new T())
        {
            var tableNames = context.Database.SqlQuery<string>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME NOT LIKE '%Migration%'").ToList();
            foreach (var tableName in tableNames)
            {
                foreach (var t in tableNames)
                {
                    try
                    {

                        if (context.Database.ExecuteSqlCommand(string.Format("TRUNCATE TABLE [{0}]", tableName)) == 1)
                            break;

                    }
                    catch (Exception ex)
                    {

                    }
                }
            }

            context.SaveChanges();
        }
    }

uso:

ClearDatabase<ApplicationDbContext>();

lembre-se de restabelecer seu DbContext depois disso.

Kristian Nissen
fonte
2

O seguinte funciona no banco de dados SQLite (usando o Entity Framework)

Parece que a maneira mais rápida de limpar todas as tabelas db é usando 'context.Database.ExecuteSqlCommand ("some SQL")', como destacaram alguns comentários acima. Aqui vou mostrar como redefinir a contagem de tabelas do 'índice'.

            context.Database.ExecuteSqlCommand("delete from TableA");
            context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableA'");//resets the autoindex

            context.Database.ExecuteSqlCommand("delete from TableB");
            context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableB'");//resets the autoindex 

            context.Database.ExecuteSqlCommand("delete from TableC");
            context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableC'");//resets the autoindex 

Um ponto importante é que, se você usar chaves estrangeiras em suas tabelas, deverá primeiro excluir a tabela filha antes da tabela pai, para que a sequência (hierarquia) de tabelas durante a exclusão seja importante, caso contrário, uma exceção SQLite poderá ocorrer.

Nota: var context = new YourContext ()

Matt Allen
fonte
1

Isso funciona corretamente no EF 5:

YourEntityModel myEntities = new YourEntityModel();

var objCtx = ((IObjectContextAdapter)myEntities).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [TableName]");
Hekmat
fonte
1

No EFCore (a versão que estou usando é 3.1), você pode usar o seguinte para remover todas as linhas -

context.Database.ExecuteSqlRaw("TRUNCATE TABLE [TableName]");
bsod_
fonte
0

Exclua todos os registros. Não redefina o índice primário como "truncar".

/// <summary>
/// SET - DELETE all record by table - no truncate - return deleted records
/// </summary>
public static int setListDelAllMYTABLE()
{
    // INIT
    int retObj = 0;
    using (MYDBEntities ctx = new MYDBEntities())
    {
        // GET - all record
        var tempAllRecord = ctx.MYTABLE.ToList();
        // RESET
        ctx.MYTABLE.RemoveRange(tempAllRecord);
        // SET - final save
        retObj += ctx.SaveChanges();
    }
    // RET
    return retObj;
}
Roberto Mutti
fonte
por que você retiraria todos os registros para excluí-los? extremamente ineficiente
mare
Porque o desempenho não era minha prioridade. É baseado na modularidade, portanto, se você deseja adicionar uma condição where ou verificar os dados antes de removê-los, é possível. EF6 é a ferramenta mais lento sobre o SQL I / O, então por que usar EF6 se o desempenho era a prioridade deve ser a pergunta ..
Roberto Mutti
0

No meu código, eu realmente não tinha bom acesso ao objeto Database, então você pode fazê-lo no DbSet, onde você também pode usar qualquer tipo de sql. Vai acabar assim:

var p = await _db.Persons.FromSql("truncate table Persons;select top 0 * from Persons").ToListAsync();
Thomas Koelle
fonte
0

Se MVC, você pode fazer:

public async Task<IActionResult> DeleteAll()
{
    var list = await _context.YourClass.ToListAsync();
    _context.YourClass.RemoveRange(list);
    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));
}
Diego Venâncio
fonte
0

Certifique-se de que, ao tentar excluir os pais, todos os filhos se conectem em cascata ao excluir. Ou os filhos têm chave estrangeira anulável.

E. Radokhlebov
fonte
0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
nima chapi
fonte