Entity Framework. Löschen Sie alle Zeilen in der Tabelle

280

Wie kann ich mit Entity Framework schnell alle Zeilen in der Tabelle entfernen?

Ich benutze derzeit:

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

Die Ausführung dauert jedoch lange.

Gibt es Alternativen?

Zhenia
quelle
22
Beim Lesen der Antworten frage ich mich, warum sich keiner dieser TRUNCATEAdepten Sorgen um Fremdschlüsseleinschränkungen macht.
Gert Arnold
2
Ich bin ein bisschen erstaunt darüber, wie selbstverständlich die Antworten hier sind, dass jeder Microsoft SQL Server verwendet, obwohl die Unterstützung für andere Datenbanken in Entity Framework so weit zurückreicht, wie ich Informationen über diese Frage finden kann und sicherlich mehrere Jahre älter ist . Tipp: Wenn eine Antwort Tabellennamen in SQL-Anweisungen mit eckigen Klammern (wie :) zitiert [TableName], ist sie nicht portierbar.
Mark Amery

Antworten:

293

Für diejenigen, die dies googeln und wie ich hier gelandet sind, ist dies derzeit in EF5 und EF6 der Fall:

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

Angenommen, der Kontext ist a System.Data.Entity.DbContext

Ron Sijm
quelle
20
Zu Ihrer Information, um TRUNCATE verwenden zu können, muss der Benutzer über die Berechtigung ALTER für die Tabelle verfügen. ( stackoverflow.com/questions/4735038/… )
Alex
7
@Alex Nur eine Menge Zeit mit dem Fehler "Das Objekt MyTable kann nicht gefunden werden, weil es nicht vorhanden ist oder Sie keine Berechtigungen haben." Genau aus diesem Grund werden EF-Apps selten ALTER-Berechtigungen erteilt, und die Fehlermeldung versetzt Sie wirklich in eine wilde Gänsejagd.
Chris Moschini
8
Ich hatte Probleme, da meine Tabelle Teil einer Fremdschlüsselbeziehung war, obwohl es sich um die Blatttabelle in dieser Beziehung handelte. Ich habe context.Database.ExecuteSqlCommand ("DELETE FROM [Interests]") verwendet. stattdessen
Dan Csharpster
2
Beachten Sie, dass die [Escapezeichen hier zwar spezifisch für SQL Server sind, der TRUNCATEBefehl jedoch nicht - er ist Teil von ANSI SQL und funktioniert daher in den meisten SQL-Dialekten (jedoch nicht in SQLite).
Mark Amery
207

Warnung: Das Folgende ist nur für kleine Tabellen geeignet (denken Sie an <1000 Zeilen).

Hier ist eine Lösung, die das Entity Framework (nicht SQL) zum Löschen der Zeilen verwendet, sodass sie nicht SQL Engine (R / DBM) -spezifisch ist.

Dies setzt voraus, dass Sie dies zum Testen oder in einer ähnlichen Situation tun. Entweder

  • Die Datenmenge ist gering oder
  • Die Leistung spielt keine Rolle

Rufen Sie einfach an:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

Unter der Annahme dieses Kontextes:

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

Für aufgeräumten Code können Sie die folgende Erweiterungsmethode deklarieren:

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

Dann wird das Obige:

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

Ich habe diesen Ansatz kürzlich verwendet, um meine Testdatenbank für jeden Testfalllauf zu bereinigen (es ist offensichtlich schneller, als die Datenbank jedes Mal von Grund auf neu zu erstellen, obwohl ich die Form der generierten Löschbefehle nicht überprüft habe).


Warum kann es langsam sein?

  1. EF erhält ALLE Zeilen (VotingContext.Votes)
  2. und verwenden dann ihre IDs (nicht sicher, wie, egal), um sie zu löschen.

Wenn Sie also mit einer großen Datenmenge arbeiten, beenden Sie den SQL Server-Prozess (er verbraucht den gesamten Speicher) und das Gleiche für den IIS-Prozess, da EF alle Daten auf die gleiche Weise wie SQL Server zwischenspeichert. Verwenden Sie diese nicht, wenn Ihre Tabelle ernsthafte Datenmengen enthält.

Ahmed Alejo
quelle
1
Tolle Antwort, beschleunigte das Löschen aller Zeilen um den Faktor 10! Beachten Sie, dass ich die statische Erweiterungsmethode Clear () in ClearDbSet () umbenennen musste, da ich bereits eine andere statische Erweiterungsmethode Clear () an anderer Stelle in meinem Projekt definiert hatte.
dodgy_coder
1
Das Umbenennen von @dodgy_coder ist aus dem von Ihnen angegebenen Grund nicht erforderlich, da die Erweiterungsmethode für DbSet, IDbSets und nicht für IEnumerable, IList, ICollection, ICache oder eine andere Schnittstelle gilt, für die "Clear" erforderlich wäre. Die Präferenz für die Erweiterungsmethode ist der Typ, für den die definiert sind. aber wenn das für dich klarer liest und nicht überflüssig klingt, großartig!. Ich bin froh, dass es der Leistung hilft! Prost!
Ahmed Alejo
Ich bin froh, dass Sie nur für kleine Tische angegeben haben. Ich hoffe, die Leute bekommen die Wichtigkeit Ihrer Erklärung. Da dieser Weg syntaktischer Zucker ist, ist der richtige Weg der Weg, den Ron Sijm vorgeschlagen hat. weil Sie die Daten nicht laden, bevor Sie sie entfernen. Prost, dass du diese Art und Weise gezeigt und erklärt hast.
Yedevtxt
3
Dadurch wird der Identitätsschlüssel nicht zurückgesetzt. Wenn Sie also 10 Datensätze löschen, ist der nächste immer noch 11.
MDave
89

Die Verwendung des SQL- TRUNCATE TABLEBefehls ist am schnellsten, da er für die Tabelle und nicht für einzelne Zeilen ausgeführt wird.

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

Angenommen, es dataDbist ein DbContext(kein ObjectContext), können Sie es einpacken und die folgende Methode verwenden:

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
Rudi Visser
quelle
Wenn Sie einen Berechtigungsfehler erhalten, wenn Sie dies versuchen, ändern Sie einfach TRUNCATE TABLEzuDELETE FROM
codeMonkey
1
@codeMonkey beachten Sie nur, dass dies verschiedene Operationen sind, aber (fast) den gleichen Nettoeffekt haben werden 👍
Rudi Visser
3
DELETE setzt den IDENTITY-Startwert jedoch nicht zurück. Dies kann in bestimmten Situationen problematisch sein.
Steve
43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();
user3328890
quelle
11
Dies sollte nicht verwendet werden, da Sie anstelle eines Löschvorgangs eine vollständige Auswahl und ein Löschen nach ausführen. Aus Sicht der Zeitleistung ist dies ein großes NICHT!
HellBaby
2
@HellBaby Es sei denn, es wird selten aufgerufen und daher ist die Leistung ziemlich irrelevant.
Alex
6
Auch wenn es selten genannt wird, ist das schlecht. Eine Tabelle mit nur 3000 Einträgen kann aufgrund der langsamen EF-Änderungsverfolgung bis zu 30 Sekunden dauern.
Leicht am
1
Dies ist nur für kleine Tabellen (<1000 Zeilen) geeignet
Amir Touitou
Perfekt für meine In-Memory-Datenbank in meinen Unit-Tests :)
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);
}

oder

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
Manish Mishra
quelle
1
aber wenn ich es schreibe. "query.Delete ();" - "Löschen" wird nicht erkannt
Zhenia
1
Fügen Sie die Referenz von System.Data.Entity und EntityFrameWork in Ihr aktuelles Projekt ein
Manish Mishra
Welche Erweiterungsmethode ist Löschen?
Ahmed Alejo
Soweit ich weiß, gibt es keine Erweiterungsmethode Deletefür IQueryable- ich vermute, Manish hat so etwas wie EntityFramework.Extended verwendet: github.com/loresoft/EntityFramework.Extended
null
Ich habe meine Antwort bearbeitet, früher war sie irreführend. @null, Sie haben Recht, dies .Deletewar eine benutzerdefinierte Erweiterung und in der Hitze, die Antwort zuerst zu veröffentlichen, völlig vergessen, die Definition dieser benutzerdefinierten zu erwähnen .Delete. :)
Manish Mishra
23

Sie können das ohne Foreach tun

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

Dadurch werden alle Zeilen entfernt

Omid Farvid
quelle
Wird es Navigationseigenschaftselemente abschneiden?
John Deer
19

Dadurch wird die Verwendung von SQL vermieden

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

Ich bin auf diese Frage gestoßen, als ich mich mit einem bestimmten Fall befassen musste: Vollständige Aktualisierung des Inhalts in einer "Blatt" -Tabelle (keine darauf zeigenden FKs). Dies beinhaltete das Entfernen aller Zeilen und das Einfügen neuer Zeileninformationen und sollte transaktional erfolgen (ich möchte nicht mit einer leeren Tabelle enden, wenn Einfügungen aus irgendeinem Grund fehlschlagen).

Ich habe das versucht public static void Clear<T>(this DbSet<T> dbSet) Ansatz , aber neue Zeilen werden nicht eingefügt. Ein weiterer Nachteil ist, dass der gesamte Prozess langsam ist, da die Zeilen einzeln gelöscht werden.

Also habe ich auf TRUNCATEAnnäherung umgestellt , da es viel schneller und auch ROLLBACKable ist . Es setzt auch die Identität zurück.

Beispiel mit Repository-Muster:

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

Wie bereits erwähnt, kann diese Lösung natürlich nicht von Tabellen verwendet werden, auf die durch Fremdschlüssel verwiesen wird (TRUNCATE schlägt fehl).

Alexei
quelle
Zwei Kommentare: 1. Der Tabellenname sollte in [...] eingeschlossen werden. Eine meiner Klassen / Tabellen heißt "Transaction", ein SQL-Schlüsselwort. 2. Wenn das Ziel darin besteht, alle Tabellen in einer Datenbank für Komponententests zu löschen, können Probleme mit Fremdschlüsseleinschränkungen leicht umgangen werden, indem die Tabellen so angeordnet werden, dass sie so verarbeitet werden, dass untergeordnete Tabellen vor übergeordneten Tabellen abgeschnitten werden.
Christoph
@Christoph - 1. Ja, das ist wahr. Ich habe das verpasst, weil ich Tabellen immer benenne, um Schlüsselwörter zu vermeiden, da dies zu Problemen führen kann. 2. Wenn ich mich richtig erinnere, können Tabellen, auf die von FKs verwiesen wird, nicht abgeschnitten werden (SQL Server-Auslösungen Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), auch wenn sie leer sind. Daher müssen FKs gelöscht und neu erstellt werden, damit sie TRUNCATEtrotzdem verwendet werden können.
Alexei
5

wenn

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

Ursachen

Die Tabelle 'MyTable' kann nicht abgeschnitten werden, da sie durch eine FOREIGN KEY-Einschränkung referenziert wird.

Ich benutze das :

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }
Zakos
quelle
1
Wenn Sie Fremdschlüsseleinschränkungen haben: (1) SQL könnte zu "DELETE FROM MyTable" vereinfacht werden. (2) Dadurch wird der ID-Zähler nicht zurückgesetzt, wenn Sie ihn auf automatische Inkrementierung eingestellt haben (Abschneiden tut dies).
RGH
5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();
DBB
quelle
4

Wenn Sie Ihre gesamte Datenbank löschen möchten.

Aufgrund der Fremdschlüsseleinschränkungen ist es wichtig, in welcher Reihenfolge die Tabellen abgeschnitten werden. Dies ist eine Möglichkeit, diese Sequenz brutal zu erzwingen.

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

Verwendung:

ClearDatabase<ApplicationDbContext>();

Denken Sie daran, Ihren DbContext danach erneut zu installieren.

Kristian Nissen
quelle
2

Das Folgende funktioniert in der SQLite-Datenbank (unter Verwendung von Entity Framework).

Es scheint, dass der schnellste Weg, alle DB-Tabellen zu löschen, die Verwendung von 'context.Database.ExecuteSqlCommand ("some SQL")' ist, wie einige der obigen Kommentare ebenfalls hervorheben. Hier werde ich zeigen, wie man auch die 'Index'-Anzahl von Tabellen zurücksetzt.

            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 

Ein wichtiger Punkt ist, dass Sie, wenn Sie Fremdschlüssel in Ihren Tabellen verwenden, zuerst die untergeordnete Tabelle vor der übergeordneten Tabelle löschen müssen. Daher ist die Reihenfolge (Hierarchie) der Tabellen während des Löschens wichtig, da sonst eine SQLite-Ausnahme auftreten kann.

Hinweis: var context = new YourContext ()

Matt Allen
quelle
1

Dies funktioniert ordnungsgemäß in EF 5:

YourEntityModel myEntities = new YourEntityModel();

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

In EFCore (Version, die ich verwende, ist 3.1) können Sie Folgendes verwenden, um alle Zeilen zu entfernen:

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

Löschen Sie alle Datensätze. Setzen Sie den Primärindex nicht wie "Abschneiden" zurück.

/// <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
quelle
Warum sollten Sie alle Datensätze herausziehen, um sie zu löschen? extrem ineffizient
Stute
Weil Leistung nicht meine Priorität war. Es basiert auf Modularität. Wenn Sie also eine Where-Bedingung hinzufügen oder die Daten überprüfen möchten, bevor Sie sie entfernen, können Sie dies tun. EF6 ist das langsamste Tool für SQL I / O. Warum sollte die Verwendung von EF6, wenn Leistung Priorität hat, die Frage sein?
Roberto Mutti
0

In meinem Code hatte ich keinen wirklich guten Zugriff auf das Datenbankobjekt, daher können Sie dies auf dem DbSet tun, wo Sie auch jede Art von SQL verwenden dürfen. Es wird so enden:

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

Wenn MVC, können Sie:

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
quelle
0

Stellen Sie sicher, dass beim Löschen des übergeordneten Elements alle untergeordneten Elemente beim Löschen kaskadiert werden. Oder Kinder haben einen nullbaren Fremdschlüssel.

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