Wie soll ich alle Elemente in einem DbSet entfernen?

75

Was ist der beste Weg, um alle Elemente in einem System.Data.Entity.DbSet mit Entity Framework 4.3 zu entfernen?

aknuds1
quelle

Antworten:

102
dbContext.Database.ExecuteSqlCommand("delete from MyTable");

(Im Ernst.)

Das Problem ist, dass EF keine Stapelbefehle unterstützt und die einzige Möglichkeit, alle Entitäten in einem Satz ohne direkte DML zu löschen, darin besteht, Folgendes zu tun:

foreach (var entity in dbContext.MyEntities)
    dbContext.MyEntities.Remove(entity);
dbContext.SaveChanges();

Oder vielleicht ein bisschen billiger, um das Laden vollständiger Entitäten zu vermeiden:

foreach (var id in dbContext.MyEntities.Select(e => e.Id))
{
    var entity = new MyEntity { Id = id };
    dbContext.MyEntities.Attach(entity);
    dbContext.MyEntities.Remove(entity);
}
dbContext.SaveChanges();

In beiden Fällen müssen Sie jedoch alle Entitäten oder alle Schlüsseleigenschaften laden und die Entitäten einzeln aus der Gruppe entfernen. Wenn Sie SaveChangesEF aufrufen, werden außerdem n (= Anzahl der Entitäten in der Menge) DELETE-Anweisungen an die Datenbank gesendet, die auch einzeln in der Datenbank ausgeführt werden (in einer einzelnen Transaktion).

Daher ist Direct SQL für diesen Zweck eindeutig vorzuziehen, da Sie nur eine einzige DELETE-Anweisung benötigen.

Slauma
quelle
2
Sie benötigen rufen SaveChangesnach dbContext.Database.ExecuteSqlCommand?
PeterX
Das eigentliche Problem ist, wie der DbContext Zugriff auf alle seine DbSets / Entities bietet. Es gibt keine Möglichkeit, sie zu durchlaufen, es sei denn, Sie verwenden Reflexion. Haben Sie das gedacht, als Sie sich auf "MyEntities" bezogen haben?
Veverke
2
Was ist mit RemoveRangestatt Remove? Würden Sie sagen, "das wurde nur in EF 6 eingeführt und OP nach EF 4 gefragt. *"?
Die rote Erbse
32

Alter Beitrag, aber es gibt jetzt eine RemoveRange-Methode:

    dbContext.MyEntities.RemoveRange(dbContext.MyEntities);
    dbContext.SaveChanges();
Joeystdio
quelle
1
Ich verwende diese Antwort, weil der Code einfach und klar ist. Ich bin mir sicher, dass es effizientere Möglichkeiten gibt, dies zu tun, aber ich arbeite mit einem Azure WebJob, der über Nacht ausgeführt wird. Daher ist dies derzeit kein Problem.
Phil Ringsmuth
Das Problem mit RemoveRange besteht darin, dass Sie immer noch alle Entitäten aus der Datenbank (oder zumindest die IDs) abfragen müssen, da diese Methode nur ein Wrapper um einen foreach ist.
Szörényi Ádám
18

Hier ist eine andere Möglichkeit, wie Sie dies im Code tun können.

public static class Extensions
{
    public static void DeleteAll<T>(this DbContext context)
        where T : class
    {
        foreach (var p in context.Set<T>())
        {
            context.Entry(p).State = EntityState.Deleted;
        }
    }
}

So rufen Sie die Methode auf und löschen die Menge:

myDbContext.DeleteAll<MyPocoClassName>();
Tim Cooke
quelle
3

Wenn Sie alle Elemente entfernen möchten, ohne SQL zu schreiben, und nur einen einzelnen Datenbankaufruf ausführen möchten

Die erweiterte Entity Framework-Bibliothek bietet eine Batch-Löschmethode .

context.Users.Delete();
Anestis Kivranoglou
quelle
2

In der akzeptierten Antwort wird nur die folgende Methode erwähnt:

context.Database.ExecuteSqlCommand("delete from MyTable");

Ich habe es geschafft, eine Methode zu schreiben, mit der Sie vermeiden können, alle Entitäten zu laden, sie dann zu durchlaufen und stattdessen ExecuteSqlCommand zu verwenden.

Angenommen, Sie verwenden eine Arbeitseinheit, wobei der Kontext DbContext ist:

using System.Data.Entity.Core.Objects;
using System.Text.RegularExpressions;

public void DeleteAll()
{
    ObjectContext objectContext = ( (IObjectContextAdapter)context ).ObjectContext;
    string sql = objectContext.CreateObjectSet<T>().ToTraceString();
    Regex regex = new Regex( "FROM (?<table>.*) AS" );
    Match match = regex.Match( sql );
    string tableName = match.Groups[ "table" ].Value;

    context.Database.ExecuteSqlCommand( string.Format( "delete from {0}", tableName ) );
}

Der erste Codeblock ruft den in der ExecuteSqlCommand- Methode benötigten Tabellennamen ab .

Verwendung:

using ( var context = new UnitOfWork() )
{
    context.MyRepository.DeleteAll();
}

Sie müssen nicht anrufen

context.SaveChanges()
Ryfcia
quelle
1

Wenn Sie mit einer Arbeitseinheit und einem generischen Repository arbeiten, ist Folgendes möglicherweise hilfreich

public virtual void DeleteWhere(Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<TEntity> query = dbSet;
            if (filter != null)
            {
                query = query.Where(filter);
            }
            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            foreach (var entity in query)
            {
                context.Entry(entity).State = EntityState.Deleted;
            }
        }

Verwendung:

uow.myRepositoryName.DeleteWhere(u => u.RoomId == roomId);
uow.Save();
Fred Johnson
quelle