Kerangka Entitas. Hapus semua baris dalam tabel

280

Bagaimana saya bisa menghapus semua baris dalam tabel dengan cepat menggunakan Entity Framework?

Saat ini saya menggunakan:

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

Namun, butuh waktu lama untuk dieksekusi.

Apakah ada alternatif lain?

Zhenia
sumber
22
Membaca jawaban saya bertanya-tanya mengapa tidak satu pun dari TRUNCATEpakar ini khawatir tentang batasan kunci asing.
Gert Arnold
2
Saya agak kagum dengan bagaimana jawaban di sini diterima begitu saja bahwa semua orang menggunakan Microsoft SQL Server, meskipun dukungan untuk database lain di Entity Framework kembali sejauh saya dapat menemukan informasi tentang dan tentu saja mendahului pertanyaan ini beberapa tahun kemudian. . Kiat: jika jawaban mengutip nama tabel dalam pernyataan SQL dengan tanda kurung (seperti:) [TableName], itu tidak portabel.
Mark Amery

Jawaban:

293

Bagi mereka yang googling ini dan berakhir di sini seperti saya, ini adalah bagaimana Anda melakukannya di EF5 dan EF6:

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

Dengan asumsi konteksnya adalah a System.Data.Entity.DbContext

Ron Sijm
sumber
20
FYI, untuk menggunakan TRUNCATE, pengguna harus memiliki izin ALTER di atas meja. ( stackoverflow.com/questions/4735038/… )
Alex
7
@ Alex Hanya membuang banyak waktu pada kesalahan "Tidak dapat menemukan objek MyTable karena tidak ada atau Anda tidak memiliki izin." untuk alasan yang tepat - izin ALTER jarang diberikan ke aplikasi EF, dan pesan kesalahan benar-benar mengirim Anda dengan sia-sia.
Chris Moschini
8
Saya memiliki masalah karena meja saya adalah bagian dari hubungan kunci asing, meskipun itu adalah tabel daun dalam hubungan itu. Saya akhirnya menggunakan context.Database.ExecuteSqlCommand ("DELETE FROM [Interests]"); sebaliknya
Dan Csharpster
2
Perhatikan bahwa sementara [lolos di sini khusus untuk SQL Server, TRUNCATEperintahnya tidak - itu bagian dari ANSI SQL dan akan bekerja di sebagian besar dialek SQL (meskipun bukan SQLite).
Mark Amery
207

Peringatan: Berikut ini hanya cocok untuk tabel kecil (pikirkan <1000 baris)

Berikut ini adalah solusi yang menggunakan kerangka kerja entitas (bukan SQL) untuk menghapus baris, sehingga tidak spesifik untuk SQL Engine (R / DBM).

Ini mengasumsikan bahwa Anda melakukan ini untuk pengujian atau situasi serupa. Antara

  • Jumlah data kecil atau
  • Performanya tidak masalah

Cukup telepon:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

Dengan asumsi konteks ini:

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

Untuk kode yang lebih rapi, Anda dapat mendeklarasikan metode ekstensi berikut:

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

Maka di atas menjadi:

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

Saya baru-baru ini menggunakan pendekatan ini untuk membersihkan database pengujian saya untuk setiap proses testcase (ini jelas lebih cepat daripada menciptakan kembali DB dari awal setiap kali, meskipun saya tidak memeriksa bentuk perintah delete yang dihasilkan).


Mengapa bisa lambat?

  1. EF akan mendapatkan SEMUA baris (VotingContext.Votes)
  2. dan kemudian akan menggunakan ID mereka (tidak yakin persis bagaimana, tidak masalah), untuk menghapusnya.

Jadi jika Anda bekerja dengan jumlah data yang serius Anda akan mematikan proses SQL server (itu akan menghabiskan semua memori) dan hal yang sama untuk proses IIS karena EF akan men-cache semua data dengan cara yang sama seperti SQL server. Jangan gunakan yang ini jika tabel Anda mengandung jumlah data yang serius.

Ahmed Alejo
sumber
1
Jawaban bagus, mempercepat saya menghapus semua baris kode dengan faktor 10! Perhatikan bahwa saya harus mengganti nama metode ekstensi statis Clear () menjadi sesuatu seperti ClearDbSet () karena saya sudah memiliki metode ekstensi statis Clear () lain yang ditentukan di tempat lain dalam proyek saya.
dodgy_coder
1
Pengubahan nama @dodgy_coder tidak diperlukan karena alasan yang Anda berikan, karena metode ekstensi untuk DbSet, IDbSet, dan bukan IEnumerable, IList, ICollection, ICache atau antarmuka lain yang diperlukan "Clear". preferensi untuk metode ekstensi adalah jenis yang didefinisikan. tetapi jika itu terdengar lebih jelas bagi Anda dan tidak terdengar berlebihan, Hebat !. saya senang ini membantu kinerja secara bijaksana! Bersulang!
Ahmed Alejo
Saya senang Anda menyatakan untuk meja kecil saja. Saya harap orang-orang mendapatkan pentingnya penjelasan Anda. karena cara ini adalah gula sintaksis, cara yang tepat adalah cara yang disarankan Ron Sijm. karena Anda tidak memuat data sebelum menghapusnya. tepuk tangan untuk menunjukkan dan menjelaskan cara melakukannya.
yedevtxt
3
Ini tidak mengatur ulang kunci identitas. Jadi, jika Anda menghapus 10 catatan, yang berikutnya akan tetap 11.
MDave
89

Menggunakan TRUNCATE TABLEperintah SQL akan menjadi yang tercepat karena beroperasi di atas meja dan bukan pada baris individual.

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

Dengan asumsi dataDbadalah DbContext(bukan an ObjectContext), Anda dapat membungkusnya dan menggunakan metode seperti ini:

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
Rudi Visser
sumber
Jika Anda mendapatkan kesalahan izin saat Anda mencoba ini, ubah saja TRUNCATE TABLEkeDELETE FROM
codeMonkey
1
@codeMonkey perhatikan bahwa ini adalah operasi yang berbeda, tetapi akan memiliki (hampir) efek bersih yang sama 👍
Rudi Visser
3
Tetapi DELETE tidak mengatur ulang IDENTITY seed. Ini bisa bermasalah dalam situasi tertentu.
Steve
43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();
pengguna3328890
sumber
11
Ini tidak boleh digunakan karena Anda menjalankan pilih penuh dan menghapus setelah bukan hanya menghapus. Dari tampilan kinerja waktu, ini TIDAK besar!
HellBaby
2
@ HellBaby Kecuali itu jarang disebut dan dengan demikian kinerjanya sangat tidak relevan.
Alex
6
Bahkan jika itu jarang disebut ini buruk. Tabel dengan hanya 3000 entri dapat memakan waktu hingga 30 detik karena lambatnya pelacakan perubahan EF.
Sedikit
1
Ini hanya cocok untuk meja kecil (<1000 baris)
Amir Touitou
Sempurna untuk Database dalam-memori saya dalam Tes Unit saya :)
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);
}

atau

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
Manish Mishra
sumber
1
tetapi ketika saya menulisnya. "query.Delete ();" - "Hapus" tidak dikenali
Zhenia
1
tambahkan referensi System.Data.Entity dan EntityFrameWork di proyek Anda saat ini
Manish Mishra
Apa metode ekstensi itu Hapus?
Ahmed Alejo
Sejauh yang saya tahu tidak ada metode ekstensi Deletedi IQueryable- Saya menduga Manish menggunakan sesuatu seperti EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
nol
Saya sudah mengedit jawaban saya, sebelumnya itu menyesatkan. @null, Anda benar, ini .Deleteadalah ekstensi khusus dan dalam panasnya memposting jawabannya, benar-benar lupa menyebutkan definisi kustom ini .Delete. :)
Manish Mishra
23

Anda dapat melakukannya tanpa Foreach

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

Ini akan menghapus semua baris

Omid Farvid
sumber
Apakah ini akan memotong item properti navigasi?
John Deer
19

Ini menghindari penggunaan sql apa pun

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

Saya menemukan pertanyaan ini ketika saya harus berurusan dengan kasus tertentu: memperbarui konten sepenuhnya dalam tabel "daun" (tidak ada FK menunjuk ke sana). Ini melibatkan menghapus semua baris dan meletakkan informasi baris baru dan itu harus dilakukan secara transaksi (saya tidak ingin berakhir dengan tabel kosong, jika memasukkan gagal karena alasan apa pun).

Saya sudah mencoba public static void Clear<T>(this DbSet<T> dbSet)pendekatannya, tetapi baris baru tidak dimasukkan. Kelemahan lain adalah bahwa seluruh proses lambat, karena baris dihapus satu per satu.

Jadi, saya telah beralih ke TRUNCATEpendekatan, karena jauh lebih cepat dan juga ROLLBACKable . Ini juga mengatur ulang identitas.

Contoh menggunakan pola repositori:

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

Tentu saja, seperti yang telah disebutkan, solusi ini tidak dapat digunakan oleh tabel yang dirujuk oleh kunci asing (TRUNCATE gagal).

Alexei
sumber
Dua komentar: 1. Nama tabel harus dibungkus dengan [...]. Salah satu kelas / tabel saya disebut "Transaksi", yang merupakan kata kunci SQL. 2. Jika tujuannya adalah untuk menghapus semua tabel dalam DB untuk pengujian unit, masalah dengan batasan kunci asing dapat dengan mudah diselesaikan dengan memesan tabel untuk memprosesnya sedemikian rupa sehingga tabel anak dapat dipotong sebelum tabel induk.
Christoph
@Christoph - 1. Ya, itu benar. Saya melewatkan itu, karena saya selalu memberi nama tabel untuk menghindari kata kunci karena dapat menyebabkan masalah. 2. Jika saya ingat dengan benar, tabel yang direferensikan oleh FK tidak dapat dipotong (SQL Server melempar Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), bahkan jika mereka kosong, sehingga FK harus dijatuhkan dan diciptakan kembali agar dapat digunakan TRUNCATE.
Alexei
5

jika

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

penyebab

Tidak dapat memotong tabel 'MyTable' karena sedang direferensikan oleh batasan KUNCI ASING.

Saya menggunakan ini:

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }
Zakos
sumber
1
Jika Anda memiliki batasan kunci asing: (1) SQL dapat disederhanakan menjadi "DELETE FROM MyTable". (2) Ini tidak akan mengatur ulang penghitung Id jika Anda telah mengaturnya ke autoincrement (terpotong tidak).
RGH
5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();
DBB
sumber
4

Jika Anda ingin menghapus seluruh basis data Anda.

Karena batasan kunci asing, penting urutan tabel mana yang terpotong. Ini adalah cara untuk menguatkan urutan ini.

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

pemakaian:

ClearDatabase<ApplicationDbContext>();

jangan lupa untuk mengembalikan konteks DbContext Anda setelah ini.

Kristian Nissen
sumber
2

Berikut ini berfungsi pada database SQLite (menggunakan Entity Framework)

Tampaknya cara tercepat untuk menghapus semua tabel db menggunakan 'context.Database.ExecuteSqlCommand ("some SQL")', karena beberapa komentar di atas juga disorot. Di sini saya akan menunjukkan cara mengatur ulang jumlah tabel 'indeks' juga.

            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 

Satu poin penting adalah bahwa jika Anda menggunakan kunci asing di tabel Anda, Anda harus terlebih dahulu menghapus tabel anak sebelum tabel induk, sehingga urutan (hierarki) tabel selama penghapusan adalah penting, jika tidak, pengecualian SQLite dapat terjadi.

Catatan: var context = newContext Anda ()

Matt Allen
sumber
1

Ini berfungsi dengan baik di EF 5:

YourEntityModel myEntities = new YourEntityModel();

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

Di EFCore (versi yang saya gunakan adalah 3.1) Anda dapat menggunakan yang berikut untuk menghapus semua baris -

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

Hapus semua catatan. Jangan mengatur ulang indeks utama seperti "truncate".

/// <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
sumber
mengapa Anda menarik semua catatan untuk menghapusnya? sangat tidak efisien
mare
Karena kinerja bukan prioritas saya. Ini didasarkan pada modularitas, jadi jika Anda ingin menambahkan kondisi di mana atau memeriksa data sebelum menghapusnya, Anda bisa. EF6 adalah alat paling lambat tentang SQL I / O, jadi mengapa menggunakan EF6 jika kinerja adalah prioritas harus menjadi pertanyaan ..
Roberto Mutti
0

Dalam kode saya, saya tidak benar-benar memiliki akses bagus ke objek Database, sehingga Anda dapat melakukannya pada DbSet di mana Anda juga diizinkan untuk menggunakan segala jenis sql. Ini akan berakhir seperti ini:

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

Jika MVC, Anda dapat melakukan:

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

Pastikan ketika Anda mencoba untuk menghapus orangtua semua anak akan mengalir pada penghapusan. Atau anak-anak memiliki kunci asing yang dapat dibatalkan.

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