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?


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


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.


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]");

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

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]");
}

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


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

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).


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

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

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.


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


1

Ini berfungsi dengan baik di EF 5:

YourEntityModel myEntities = new YourEntityModel();

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

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]");

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

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

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

0

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


0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.