Entity Framework. Elimina tutte le righe nella tabella


280

Come posso rimuovere rapidamente tutte le righe nella tabella usando Entity Framework?

Attualmente sto usando:

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

Tuttavia, l'esecuzione richiede molto tempo.

Ci sono alternative?


22
Leggendo le risposte, mi chiedo perché nessuno di questi TRUNCATEadepti si preoccupi dei vincoli delle chiavi esterne.
Gert Arnold,

2
Sono un po 'stupito dal modo in cui le risposte qui danno per scontato che tutti stiano usando Microsoft SQL Server, anche se il supporto per altri database in Entity Framework risale a quanto posso trovare informazioni e sicuramente precede questa domanda di diversi anni . Suggerimento: se una risposta cita i nomi delle tabelle nelle istruzioni SQL con parentesi quadre (come:) [TableName], non è portatile.
Mark Amery,

Risposte:


293

Per quelli che stanno cercando su Google e sono finiti qui come me, ecco come lo fai attualmente in EF5 ed EF6:

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

Supponendo che il contesto sia a System.Data.Entity.DbContext


20
Cordiali saluti, per utilizzare TRUNCATE l'utente deve disporre dell'autorizzazione ALTER sulla tabella. ( stackoverflow.com/questions/4735038/… )
Alex

7
@Alex Ho perso un sacco di tempo con l'errore "Impossibile trovare l'oggetto MyTable perché non esiste o non disponi delle autorizzazioni." per questo motivo esatto: le autorizzazioni ALTER sono raramente concesse alle app EF e il messaggio di errore ti manda davvero su una caccia all'oca selvatica.
Chris Moschini,

8
Ho avuto problemi poiché il mio tavolo faceva parte di una relazione di chiave esterna, anche se era la tabella foglia in quella relazione. Ho finito con context.Database.ExecuteSqlCommand ("DELETE FROM [Interests]"); invece
Dan Csharpster l'

2
Si noti che mentre le [escape qui sono specifiche di SQL Server, il TRUNCATEcomando non lo è - fa parte di ANSI SQL e quindi funzionerà nella maggior parte dei dialetti SQL (sebbene non in SQLite).
Mark Amery,

207

Avvertenza: quanto segue è adatto solo per piccole tabelle (si pensi a <1000 righe)

Ecco una soluzione che utilizza il framework di entità (non SQL) per eliminare le righe, quindi non è specifico di SQL Engine (R / DBM).

Questo presuppone che tu lo stia facendo per un test o una situazione simile. O

  • La quantità di dati è piccola o
  • Le prestazioni non contano

Chiama semplicemente:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

Supponendo questo contesto:

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;}
}

Per il codice più ordinato puoi dichiarare il seguente metodo di estensione:

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

Quindi quanto sopra diventa:

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

Recentemente ho usato questo approccio per ripulire il mio database di test per ogni esecuzione di testcase (ovviamente è più veloce che ricreare il DB da capo ogni volta, anche se non ho controllato la forma dei comandi di eliminazione che sono stati generati).


Perché può essere lento?

  1. EF otterrà TUTTE le righe (VotingContext.Votes)
  2. e poi useranno i loro ID (non sono sicuro di come, non importa), per eliminarli.

Quindi, se stai lavorando con una grande quantità di dati, ucciderai il processo del server SQL (consumerà tutta la memoria) e la stessa cosa per il processo IIS poiché EF memorizzerà nella cache tutti i dati allo stesso modo del server SQL. Non usare questo se la tua tabella contiene una grande quantità di dati.


1
Ottima risposta, ho velocizzato il mio codice di eliminazione di tutte le righe di un fattore 10! Nota che ho dovuto rinominare il metodo di estensione statica Clear () in qualcosa come ClearDbSet () poiché avevo già un altro metodo di estensione statica Clear () definito altrove nel mio progetto.
dodgy_coder

1
La ridenominazione di @dodgy_coder non è necessaria per il motivo fornito, poiché il metodo di estensione è per DbSet, IDbSet e non IEnumerable, IList, ICollection, ICache o qualsiasi altra interfaccia che "Clear" sarebbe richiesta. le preferenze per il metodo di estensione sono il tipo su cui sono definite. ma se ti sembra più chiaro e non sembra ridondante, fantastico !. sono contento che aiuti le prestazioni sagge! Saluti!
Ahmed Alejo,

Sono contento che tu l'abbia dichiarato solo per piccoli tavoli. spero che le persone ottengano l'importanza della tua spiegazione. poiché in questo modo si tratta di zucchero sintattico, il modo corretto è quello suggerito da Ron Sijm. perché non carichi i dati prima di rimuoverli. applausi però per aver mostrato e spiegato questo modo di farlo.
yedevtxt,

3
Ciò non reimposta la chiave di identità. Quindi, se
cancelli

89

L'uso del TRUNCATE TABLEcomando SQL sarà il più veloce in quanto opera sulla tabella e non su singole righe.

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

Supponendo che dataDbsia un DbContext(non un ObjectContext), puoi avvolgerlo e usare il metodo in questo modo:

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");

Se si verifica un errore di autorizzazione quando si prova questo, basta passare TRUNCATE TABLEaDELETE FROM
codeMonkey

1
@codeMonkey basta prendere nota del fatto che si tratta di operazioni diverse, ma avranno (quasi) lo stesso effetto netto 👍
Rudi Visser,

3
DELETE non reimposta il seme IDENTITY. Questo potrebbe essere problematico in determinate situazioni.
Steve,

43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();

11
Questo non dovrebbe essere usato perché esegui una selezione completa e un'eliminazione dopo anziché solo un'eliminazione. Dal punto di vista delle prestazioni nel tempo, questo NON è un grande!
HellBaby,

2
@HellBaby A meno che non venga chiamato di rado e quindi le prestazioni sono abbastanza irrilevanti.
Alex,

6
Anche se raramente si chiama questo è male. Una tabella con solo 3000 voci potrebbe richiedere fino a 30 secondi a causa della lentezza del rilevamento delle modifiche EF.
Leggero

1
Questo è adatto solo per piccoli tavoli (<1000 righe)
Amir Touitou,

Perfetto per il mio database in memoria nei test delle mie unità :)
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);
}

o

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}

1
ma quando lo scrivo. "query.Delete ();" - "Elimina" non riconosciuto
Zhenia

1
aggiungi il riferimento di System.Data.Entity e EntityFrameWork nel tuo progetto attuale
Manish Mishra

Quale metodo di estensione è Elimina?
Ahmed Alejo,

Per quanto ne so, non esiste un metodo di estensione Deletesu IQueryable- sto cercando di indovinare Manish stava usando qualcosa come EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
nulla

Ho modificato la mia risposta, in precedenza era fuorviante. @null, hai ragione, questa .Deleteera un'estensione personalizzata e nel calore di pubblicare prima la risposta, ho completamente dimenticato di menzionare la definizione di questa abitudine .Delete. :)
Manish Mishra,

23

Puoi farlo senza Foreach

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

Questo rimuoverà tutte le righe


Troncerà gli elementi delle proprietà di navigazione?
John Deer,

19

Questo evita di usare qualsiasi sql

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

9

Mi sono imbattuto in questa domanda quando ho dovuto affrontare un caso particolare: l'aggiornamento completo dei contenuti in una tabella "foglia" (nessun FK che lo indica). Ciò ha comportato la rimozione di tutte le righe e l'inserimento di nuove informazioni sulle righe e dovrebbe essere eseguito in modo transazionale (non voglio finire con una tabella vuota, se gli inserimenti falliscono per qualsiasi motivo).

Ho provato il public static void Clear<T>(this DbSet<T> dbSet) approccio, ma non sono state inserite nuove righe. Un altro svantaggio è che l'intero processo è lento, poiché le righe vengono eliminate una per una.

Quindi, sono passato TRUNCATEall'approccio, poiché è molto più veloce ed è anche ROLLBACKable . Ripristina anche l'identità.

Esempio utilizzando il modello di repository:

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();

Naturalmente, come già accennato, questa soluzione non può essere utilizzata dalle tabelle a cui fanno riferimento chiavi esterne (TRUNCATE ha esito negativo).


Due commenti: 1. Il nome della tabella deve essere racchiuso tra [...]. Una delle mie classi / tabelle si chiama "Transazione", che è una parola chiave SQL. 2. Se l'obiettivo è quello di cancellare tutte le tabelle in un DB per unit test, i problemi con vincoli di chiave esterna possono essere facilmente risolti ordinando le tabelle per elaborarle in modo che le tabelle figlio vengano troncate prima delle tabelle padre.
Christoph,

@Christoph - 1. Sì, è vero. Mi è mancato, perché nomina sempre le tabelle per evitare parole chiave in quanto ciò potrebbe causare problemi. 2. Se ricordo bene, le tabelle a cui fanno riferimento gli FK non possono essere troncate (genera SQL Server Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), anche se sono vuote, quindi gli FK devono essere eliminati e ricreati per poter TRUNCATEcomunque utilizzare .
Alexei,

5

Se

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

cause

Impossibile troncare la tabella "MyTable" perché a cui fa riferimento un vincolo FOREIGN KEY.

Io lo uso questo:

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }

1
Se hai vincoli di chiave esterna: (1) SQL potrebbe essere semplificato in "ELIMINA DA TABELLA". (2) Questo non resetterà il contatore Id se è impostato su autoincremento (troncato fa).
RGH,

5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();

4

Se si desidera cancellare l'intero database.

A causa dei vincoli di chiave esterna è importante quale sequenza le tabelle vengono troncate. Questo è un modo per rinforzare questa sequenza.

    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();
        }
    }

utilizzo:

ClearDatabase<ApplicationDbContext>();

ricorda di reintegrare il tuo DbContext dopo questo.


2

Il seguente funziona sul database SQLite (usando Entity Framework)

Sembra che il modo più veloce per cancellare tutte le tabelle db sia usare 'context.Database.ExecuteSqlCommand ("some SQL")', come anche alcuni commenti sopra evidenziati. Qui ho intenzione di mostrare come reimpostare anche il conteggio 'indice' delle tabelle.

            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 

Un punto importante è che se si utilizzano chiavi esterne nelle tabelle, è necessario prima eliminare la tabella figlio prima della tabella padre, quindi la sequenza (gerarchia) delle tabelle durante l'eliminazione è importante, altrimenti potrebbe verificarsi un'eccezione SQLite.

Nota: var context = new YourContext ()


1

Funziona correttamente in EF 5:

YourEntityModel myEntities = new YourEntityModel();

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

1

In EFCore (la versione che sto usando è la 3.1) puoi usare quanto segue per rimuovere tutte le righe -

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

0

Elimina tutti i record. Non ripristinare l'indice primario come "troncare".

/// <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;
}

perché dovresti estrarre tutti i record per eliminarli? estremamente inefficiente
cavalla

Perché la performance non era la mia priorità. Si basa sulla modularità, quindi se si desidera aggiungere una condizione where o controllare i dati prima di rimuoverlo, è possibile. EF6 è lo strumento più lento su I / O SQL, quindi perché utilizzare EF6 se le prestazioni fossero la priorità dovrebbe essere la domanda ..
Roberto Mutti,

0

Nel mio codice non avevo davvero un buon accesso all'oggetto Database, quindi puoi farlo sul DbSet dove puoi anche usare qualsiasi tipo di sql. Finirà in questo modo:

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

0

Se MVC, puoi fare:

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

0

Assicurarsi che quando si sta tentando di eliminare un genitore, tutti i bambini si sovrapporranno all'eliminazione. O i bambini hanno una chiave esterna nullable.


0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.