Entity Framework. Usuń wszystkie wiersze w tabeli

280

Jak mogę szybko usunąć wszystkie wiersze w tabeli za pomocą Entity Framework?

Obecnie używam:

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

Wykonanie zajmuje jednak dużo czasu.

Czy są jakieś alternatywy?

Zenia
źródło
22
Czytając odpowiedzi, zastanawiam się, dlaczego żaden z tych TRUNCATEadeptów nie martwi się ograniczeniami klucza obcego.
Gert Arnold
2
Jestem trochę zaskoczony tym, że odpowiedzi tutaj przyjmują za pewnik, że wszyscy używają Microsoft SQL Server, mimo że obsługa innych baz danych w Entity Framework sięga tak daleko, jak tylko mogę znaleźć informacje na temat tego pytania i na pewno wyprzedza to pytanie o kilka lat . Wskazówka: jeśli odpowiedź cytuje nazwy tabel w instrukcjach SQL w nawiasach kwadratowych (jak:) [TableName], nie jest przenośna.
Mark Amery

Odpowiedzi:

293

Dla tych, którzy googlują i skończyli tutaj, jak ja, tak właśnie robisz w EF5 i EF6:

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

Zakładając, że kontekst to System.Data.Entity.DbContext

Ron Sijm
źródło
20
Do Twojej wiadomości, aby użyć TRUNCATE, użytkownik musi mieć uprawnienie ZMIEŃ na stole. ( stackoverflow.com/questions/4735038/… )
Alex
7
@Alex Właśnie zmarnowałem mnóstwo czasu na błąd „Nie można znaleźć obiektu MyTable, ponieważ nie istnieje lub nie masz uprawnień”. z tego właśnie powodu - uprawnienia ALTER rzadko są przyznawane aplikacjom EF, a komunikat o błędzie naprawdę wysyła cię w dziką gęś.
Chris Moschini,
8
Miałem problemy, ponieważ mój stół był częścią relacji klucza obcego, mimo że była to tabela liści w tej relacji. I zakończyło się przy użyciu kontekstu.Database.ExecuteSqlCommand („USUŃ Z [Zainteresowań]”); zamiast
Dan Csharpster
2
Zauważ, że chociaż [tutaj znaki ucieczki są specyficzne dla SQL Server, TRUNCATEpolecenie nie jest - jest częścią ANSI SQL i dlatego będzie działać w większości dialektów SQL (choć nie SQLite).
Mark Amery
207

Ostrzeżenie: Poniższe informacje są odpowiednie tylko dla małych tabel (pomyśl <1000 wierszy)

Oto rozwiązanie, które używa struktury encji (nie SQL) do usuwania wierszy, więc nie jest ono specyficzne dla SQL Engine (R / DBM).

Zakłada się, że robisz to w celu przetestowania lub w podobnej sytuacji. Zarówno

  • Ilość danych jest niewielka lub
  • Wydajność nie ma znaczenia

Po prostu zadzwoń:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

Zakładając ten kontekst:

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

W przypadku kodu uporządkowanego można zadeklarować następującą metodę rozszerzenia:

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

Następnie powyższe staje się:

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

Ostatnio zastosowałem to podejście do czyszczenia mojej testowej bazy danych dla każdego uruchomienia zestawu testów (jest to oczywiście szybsze niż za każdym razem odtwarzanie DB od zera, chociaż nie sprawdzałem formy wygenerowanych poleceń usuwania).


Dlaczego może być powolny?

  1. EF otrzyma WSZYSTKIE wiersze (VotingContext.Votes)
  2. a następnie użyje ich identyfikatorów (nie wiem dokładnie, jak to nie ma znaczenia), aby je usunąć.

Więc jeśli pracujesz z poważną ilością danych, zabijesz proces serwera SQL (zajmie to całą pamięć) i to samo dla procesu IIS, ponieważ EF będzie buforował wszystkie dane w taki sam sposób jak serwer SQL. Nie używaj tego, jeśli twoja tabela zawiera poważne ilości danych.

Ahmed Alejo
źródło
1
Świetna odpowiedź, przyspieszyłem usuwanie kodu wszystkich wierszy 10 razy! Zauważ, że musiałem zmienić nazwę metody rozszerzenia statycznego Clear () na coś takiego jak ClearDbSet (), ponieważ miałem już inną metodę rozszerzenia statycznego Clear () zdefiniowaną gdzie indziej w moim projekcie.
dodgy_coder
1
Zmiana nazwy @dodgy_coder nie jest konieczna z podanego powodu, ponieważ metoda rozszerzenia dotyczy DbSet, IDbSet i nie jest IEnumerable, IList, ICollection, ICache ani żadnego innego interfejsu wymaganego przez „Clear”. preferowaną metodą rozszerzenia jest typ, w którym są zdefiniowane. ale jeśli brzmi to dla ciebie wyraźniej i nie brzmi zbędnie, świetnie! Cieszę się, że pomaga to w osiągach! Twoje zdrowie!
Ahmed Alejo
Cieszę się, że powiedziałeś tylko o małych stolikach. mam nadzieję, że ludzie zrozumieją znaczenie twojego wyjaśnienia. ponieważ w ten sposób jest cukier składniowy, właściwym sposobem jest sugerowany przez Rona Sijma. ponieważ nie ładujesz danych przed ich usunięciem. wiwaty za pokazanie i wyjaśnienie tego sposobu.
yedevtxt
3
To nie resetuje klucza tożsamości. Więc jeśli wyczyścisz 10 rekordów, następny będzie nadal 11.
MDave
89

Korzystanie z TRUNCATE TABLEpolecenia SQL będzie najszybsze, ponieważ działa on na tabeli, a nie na poszczególnych wierszach.

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

Zakładając, że dataDbjest DbContext(nie an ObjectContext), możesz go owinąć i użyć następującej metody:

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
Rudi Visser
źródło
Jeśli podczas próby pojawi się błąd uprawnień, po prostu zmień TRUNCATE TABLEnaDELETE FROM
codeMonkey
1
@codeMonkey po prostu zauważ, że są to różne operacje, ale będą miały (prawie) taki sam efekt netto 👍
Rudi Visser
3
Ale DELETE nie resetuje ziarna IDENTITY. W niektórych sytuacjach może to być problematyczne.
Steve
43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();
użytkownik3328890
źródło
11
Nie należy tego używać, ponieważ wykonuje się pełny wybór i usuwanie po zamiast usuwania. Z punktu widzenia wydajności czasowej jest to duże NIE!
HellBaby,
2
@HellBaby O ile nie jest to rzadko wywoływane, a zatem wydajność jest nieistotna.
Alex
6
Nawet jeśli rzadko nazywa się to, jest złe. Tabela zawierająca zaledwie 3000 wpisów może zająć ponad 30 sekund ze względu na powolne śledzenie zmian EF.
Niewielkie
1
Jest to odpowiednie tylko dla małych stolików (<1000 wierszy)
Amir Touitou,
Idealny do mojej bazy danych w pamięci w testach jednostkowych :)
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);
}

lub

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
Manish Mishra
źródło
1
ale kiedy to piszę. "query.Delete ();" - „Usuń” nie zostało rozpoznane
Żenia
1
dodaj odniesienie do System.Data.Entity i EntityFrameWork w twoim bieżącym projekcie
Manish Mishra
Jaką metodą rozszerzenia jest Usuń?
Ahmed Alejo
O ile wiem, nie jest to metoda rozszerzenie Deletena IQueryable- Zgaduję Manish używałem czegoś takiego EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
NULL
Zredagowałem swoją odpowiedź, wcześniej była ona myląca. @ null, masz rację, to .Deletebyło niestandardowe rozszerzenie i w ogniu pierwszego opublikowania odpowiedzi całkowicie zapomniałem wspomnieć o definicji tego zwyczaju .Delete. :)
Manish Mishra
23

Możesz to zrobić bez Foreach

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

Spowoduje to usunięcie wszystkich wierszy

Omid Farvid
źródło
Czy obetnie elementy właściwości nawigacji?
John Deer
19

Pozwala to uniknąć korzystania z SQL

using (var context = new MyDbContext())
{
    var itemsToDelete = context.Set<MyTable>();
    context.MyTables.RemoveRange(itemsToDelete);
    context.SaveChanges();
}
Rob Sedgwick
źródło
9

Natknąłem się na to pytanie, kiedy miałem do czynienia z konkretnym przypadkiem: pełną aktualizacją treści w tabeli „liści” (bez FK wskazujących na to). Obejmowało to usunięcie wszystkich wierszy i umieszczenie nowych informacji o wierszach i powinno to zostać wykonane transakcyjnie (nie chcę skończyć z pustą tabelą, jeśli wstawianie nie powiedzie się z jakiegokolwiek powodu).

Próbowałem public static void Clear<T>(this DbSet<T> dbSet) podejścia, ale nowe wiersze nie są wstawiane. Inną wadą jest to, że cały proces jest powolny, ponieważ wiersze są usuwane jeden po drugim.

Zmieniłem więc TRUNCATEpodejście, ponieważ jest ono znacznie szybsze i można je również ODWRÓCIĆ . Resetuje także tożsamość.

Przykład użycia wzorca repozytorium:

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

Oczywiście, jak już wspomniano, z tego rozwiązania nie mogą korzystać tabele, do których odwołują się klucze obce (TRUNCATE kończy się niepowodzeniem).

Aleksiej
źródło
Dwa komentarze: 1. Nazwa tabeli powinna być zawinięta w [...]. Jedna z moich klas / tabel nosi nazwę „Transakcja”, która jest słowem kluczowym SQL. 2. Jeśli celem jest wyczyszczenie wszystkich tabel w bazie danych do testowania jednostkowego, problemy z ograniczeniami klucza obcego można łatwo obejść, nakazując tabelom przetworzenie ich w taki sposób, aby tabele potomne zostały obcięte przed tabelami nadrzędnymi.
Christoph
@Christoph - 1. Tak, to prawda. Tęskniłem za tym, ponieważ zawsze nazywam tabele, aby uniknąć słów kluczowych, ponieważ może to prowadzić do problemów. 2. Jeśli dobrze pamiętam, tabele, do których odwołują się FK, nie mogą zostać obcięte (SQL Server rzuca Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), nawet jeśli są puste, więc FK muszą zostać usunięte i ponownie utworzone , aby móc z nich skorzystać TRUNCATE.
Aleksiej
5

Jeśli

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

przyczyny

Nie można obciąć tabeli „MyTable”, ponieważ odwołuje się do niej klucz OBCY.

Używam tego :

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }
Zakos
źródło
1
Jeśli masz ograniczenia klucza obcego: (1) SQL można uprościć do „USUŃ Z MyTable”. (2) Nie spowoduje to zresetowania licznika Id, jeśli masz ustawione automatyczne zwiększanie wartości (obcięcie robi).
RGH
5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();
DBB
źródło
4

Jeśli chcesz wyczyścić całą bazę danych.

Z powodu ograniczeń klucza obcego ważne jest, w jakiej kolejności tabele są obcinane. Jest to sposób na brutalne użycie tej sekwencji.

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

stosowanie:

ClearDatabase<ApplicationDbContext>();

pamiętaj, aby po tym przywrócić DbContext.

Kristian Nissen
źródło
2

Poniższe działa na bazie danych SQLite (przy użyciu Entity Framework)

Wydaje się, że najszybszym sposobem na wyczyszczenie wszystkich tabel db jest użycie „context.Database.ExecuteSqlCommand („ some SQL ”)”, jak również podkreślono niektóre komentarze powyżej. Tutaj pokażę, jak zresetować liczbę „indeksów” tabel.

            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 

Ważną kwestią jest to, że jeśli używasz kluczy obcych w swoich tabelach, musisz najpierw usunąć tabelę podrzędną przed tabelą nadrzędną, więc kolejność (hierarchia) tabel podczas usuwania jest ważna, w przeciwnym razie może wystąpić wyjątek SQLite.

Uwaga: var context = new YourContext ()

Matt Allen
źródło
1

Działa to poprawnie w EF 5:

YourEntityModel myEntities = new YourEntityModel();

var objCtx = ((IObjectContextAdapter)myEntities).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [TableName]");
Hekmat
źródło
1

W EFCore (wersja, której używam to 3.1) możesz użyć następujących elementów, aby usunąć wszystkie wiersze -

context.Database.ExecuteSqlRaw("TRUNCATE TABLE [TableName]");
bsod_
źródło
0

Usuń wszystkie rekordy. Nie resetuj głównego indeksu, np. „Obcinaj”.

/// <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
źródło
dlaczego wyciągnąłbyś wszystkie rekordy, aby je usunąć? wyjątkowo nieefektywna
klacz
Ponieważ wydajność nie była moim priorytetem. Opiera się na modułowości, więc jeśli chcesz dodać warunek where lub sprawdzić dane przed usunięciem, możesz to zrobić. EF6 to najwolniejsze narzędzie dotyczące SQL I / O, więc dlaczego warto używać EF6, jeśli wydajność była priorytetem, powinno być pytanie ..
Roberto Mutti
0

W moim kodzie tak naprawdę nie miałem ładnego dostępu do obiektu Database, więc możesz to zrobić na DbSet, gdzie możesz także używać dowolnego rodzaju sql. Skończy się to tak:

var p = await _db.Persons.FromSql("truncate table Persons;select top 0 * from Persons").ToListAsync();
Thomas Koelle
źródło
0

Jeśli MVC, możesz:

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
źródło
0

Upewnij się, że kiedy próbujesz usunąć rodzica, wszystkie dzieci będą kaskadowo przy usuwaniu. Lub dzieci mają zerowy klucz obcy.

E. Radochlebow
źródło
0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
nima chapi
źródło