Varlık Çerçevesi. Tablodaki tüm satırları sil


280

Entity Framework kullanarak tablodaki tüm satırları nasıl hızlı bir şekilde kaldırabilirim?

Şu anda kullanıyorum:

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

Ancak, yürütülmesi uzun zaman alır.

Alternatif var mı?


22
Cevapları okurken, bu TRUNCATEustaların hiçbirinin yabancı anahtar kısıtlamaları için neden endişelenmediğini merak ediyorum .
Gert Arnold

2
Entity Framework'teki diğer veritabanlarına yönelik destek, bu soru hakkında bilgi bulabildiğim ve kesinlikle bu soruyu birkaç yıl öncesine kadar sürdürdüğü halde, herkesin Microsoft SQL Server kullandığına dair cevapların nasıl alındığına şaşıyorum. . İpucu: Bir yanıt SQL tablolarında köşeli parantez (örneğin:) içeren tablo adlarını tırnak içine alırsa [TableName], taşınabilir değildir.
Mark Amery

Yanıtlar:


293

Bunu googling yapan ve benim gibi burada sona erenler için, şu anda EF5 ve EF6'da böyle yapıyorsunuz:

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

Bağlamın bir System.Data.Entity.DbContext


20
FYI, TRUNCATE'i kullanmak için kullanıcının masada ALTER iznine sahip olması gerekir. ( stackoverflow.com/questions/4735038/… )
Alex

7
@Alex "MyTable nesnesi bulunmadığı için ya da izinleriniz olmadığı için bulamıyorum." Hatasını bir ton harcadım. bu nedenle - ALTER izinleri nadiren EF uygulamalarına verilir ve hata mesajı sizi gerçekten vahşi bir kovalamacaya gönderir.
Chris Moschini

8
Bu ilişkideki yaprak tablosu olmasına rağmen, masamın yabancı anahtar ilişkisinin bir parçası olduğu için sorunlar yaşadım. Context.Database.ExecuteSqlCommand ("[İlgi alanları] SİL") kullanarak yara; bunun yerine
Dan Csharpster

2
Not ederken o [kaçar burada SQL Server özeldir TRUNCATEkomut değil - ANSI SQL 's parçası ve böylece (değil SQLite olsa da) en SQL lehçelerde çalışacaktır.
Mark Amery

207

Uyarı: Aşağıdaki sadece küçük tablolar için uygundur (<1000 satır düşünün)

İşte satırları silmek için varlık çerçevesi (SQL değil) kullanan bir çözüm, bu nedenle SQL Engine (R / DBM) özgü değildir.

Bu, bunu test etmek veya benzer bir durum için yaptığınızı varsayar. ya

  • Veri miktarı az veya
  • Performansın önemi yok

Sadece arayın:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

Bu bağlamı varsayarsak:

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

Düzen kodu için aşağıdaki uzantı yöntemini bildirebilirsiniz:

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

Sonra yukarıdakiler olur:

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

Son zamanlarda bu yaklaşımı her testcase çalıştırması için test veritabanımı temizlemek için kullandım.


Neden yavaş olabilir?

  1. EF TÜM satırları alacak (VotingContext.Votes)
  2. ve daha sonra kimlikleri silmek için kimliklerini kullanır (tam olarak nasıl, önemli değil).

Dolayısıyla ciddi miktarda veri ile çalışıyorsanız, EF tüm verileri SQL sunucusuyla aynı şekilde önbelleğe alacağından, SQL sunucusu işlemini (tüm belleği tüketir) ve IIS işlemi için aynı şeyi öldürürsünüz. Tablonuzda ciddi miktarda veri varsa bunu kullanmayın.


1
Harika bir cevap, tüm satır kodlarımı 10 kat hızlandırdım! Projemde başka bir yerde tanımlanmış başka bir Clear () statik uzantı yöntemine sahip olduğumdan, Clear () statik uzantı yöntemini ClearDbSet () gibi bir şeye yeniden adlandırmak zorunda olduğumu unutmayın.
dodgy_coder

1
@dodgy_coder yeniden adlandırma verdiğiniz bir nedenle gerekli değildir, çünkü uzatma yöntemi DbSet, IDbSet s içindir ve IEnumerable, IList, ICollection, ICache veya "Clear" gereken herhangi bir arabirim için değildir. uzatma yöntemi tercihi, üzerinde tanımlanmış olan tiptir. ama bu size daha net okunursa ve gereksiz gelmiyorsa, Harika !. Ben, performans akıllıca yardımcı sevindim! Şerefe!
Ahmed Alejo

Sadece küçük masalar için belirttiğinize sevindim. Umarım insanlar sizin açıklamanızın önemini kavrar. çünkü bu yol sözdizimsel şeker olduğundan, doğru yol Ron Sijm'in önerdiği şekildedir. çünkü verileri kaldırmadan önce yüklemezsiniz. göstererek ve bu şekilde açıklamak için tezahürat.
yedevtxt

3
Bu, kimlik anahtarını sıfırlamaz. Eğer 10 kaydı
silerseniz

89

SQL TRUNCATE TABLEkomutunu kullanmak , tek tek satırlarda değil, tabloda çalıştığı gibi en hızlı komut olacaktır.

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

dataDbBir DbContext(değil ObjectContext) olduğunu varsayarsak , onu sarabilir ve şu yöntemi kullanabilirsiniz:

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");

Bunu, sadece değişimi çalıştığınızda bir izinleri hata alırsanız TRUNCATE TABLEiçinDELETE FROM
codeMonkey

1
@codeMonkey sadece bunların farklı işlemler olduğunu unutmayın, ancak (neredeyse) aynı net etkiye sahip olacak 👍
Rudi Visser

3
Ancak DELETE, IDENTITY tohumunu sıfırlamaz. Bu, bazı durumlarda sorunlu olabilir.
Steve

43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();

11
Bu, yalnızca silme yerine tam seçim ve silme işlemi gerçekleştirdiğiniz için kullanılmamalıdır. Zaman-performans görünümünden bu büyük bir DEĞİLDİR!
HellBaby

2
@HellBaby Nadiren çağrılmadığı ve dolayısıyla performansın oldukça alakasız olduğu sürece.
Alex

6
Nadiren böyle adlandırılsa bile kötüdür. Yalnızca 3000 girişe sahip bir tablo, EF değişiklik izlemenin ne kadar yavaş olması nedeniyle 30 saniye kadar sürebilir.
hafif

1
Bu sadece küçük masalar için uygundur (<1000 satır)
Amir Touitou

Birim Testlerimde Bellek İçi Veritabanım için mükemmel :)
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);
}

veya

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}

1
ama ben yazdığımda. "query.Delete ();" - "Sil" tanınmıyor
Zhenia

1
Mevcut projenizdeki System.Data.Entity ve EntityFrameWork referansını ekleyin
Manish Mishra

Sil hangi uzantı yöntemi?
Ahmed Alejo

Bildiğim kadarıyla Deleteüzerinde bir uzatma yöntemi yok IQueryable- Tahmin ediyorum ki Manish EntityFramework gibi bir şey kullanıyor.Extended: github.com/loresoft/EntityFramework.Extended
null

Cevabımı düzenledim, daha önce yanıltıcıydı. @null, haklısın, bu .Deleteözel bir uzantıydı ve önce cevabı yayınlama sıcağında, bu geleneğin tanımından bahsetmeyi tamamen unuttum .Delete. :)
Manish Mishra

23

Bunu Foreach olmadan yapabilirsiniz

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

Bu, tüm satırları kaldıracak


Gezinme özelliği öğelerini kısaltacak mı?
John Deer

19

Bu herhangi bir sql kullanmaktan kaçınır

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

9

Belirli bir vakayla uğraşmak zorunda kaldığımda bu soruya rastladım: "yaprak" tablosundaki içeriğin tamamen güncellenmesi (buna işaret eden hiçbir FK). Bu, tüm satırları kaldırmak ve yeni satır bilgileri koymak ve işlemsel olarak yapılmalıdır (ekler herhangi bir nedenle başarısız olursa, boş bir tabloyla sonuçlanmak istemiyorum).

public static void Clear<T>(this DbSet<T> dbSet)Yaklaşımı denedim , ancak yeni satırlar eklenmedi. Başka bir dezavantaj, satırlar tek tek silindiği için tüm sürecin yavaş olmasıdır.

Bu yüzden, TRUNCATEçok daha hızlı ve aynı zamanda ROLLBACKable olduğu için yaklaşıma geçtim . Ayrıca kimliği sıfırlar.

Havuz desenini kullanan örnek:

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

Elbette, daha önce de belirtildiği gibi, bu çözüm yabancı anahtarlar tarafından başvurulan tablolar tarafından kullanılamaz (TRUNCATE başarısız olur).


İki yorum: 1. Tablo adı [...] içine sarılmalıdır. Sınıflarımdan / tablolarımdan birine SQL işlemi olan "Transaction" denir. 2. Amaç, birim testi için bir DB'deki tüm tabloları temizlemekse, yabancı anahtar kısıtlamalarıyla ilgili sorunlar, tabloları üst tablolardan önce alt tabloların kesileceği şekilde işlemek üzere sipariş ederek kolayca çözülebilir.
Christoph

@Christoph - 1. Evet, bu doğru. Bunu kaçırdım, çünkü her zaman sorunlara yol açabileceğinden anahtar kelimeleri önlemek için tabloları adlandırıyorum. 2. Doğru hatırlıyorsam Cannot truncate table because it is being referenced by a FOREIGN KEY constraint, boş olsalar bile FKs tarafından başvurulan tablolar kesilemez (SQL Server atar ), bu yüzden yine de kullanmak için FK'ler bırakılmalı ve yeniden oluşturulmalıdırTRUNCATE .
Alexei

5

Eğer

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

nedenleri

FOREIGN KEY kısıtlaması tarafından başvuruda bulunduğundan 'MyTable' tablosu kesilemiyor.

Bunu kullanıyorum :

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }

1
Yabancı anahtar kısıtlamalarınız varsa: (1) SQL "MyTable'DAN SİL" olarak basitleştirilebilir. (2) Otomatik sayacı ayarladıysanız (keser) Kimlik sayacını sıfırlamaz.
RGH

5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();

4

Tüm veritabanınızı temizlemek istiyorsanız.

Yabancı anahtar kısıtlamaları nedeniyle, tabloların hangi sırayla kesildiği önemlidir. Bu, bu diziyi kaba kuvvetlendirmenin bir yoludur.

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

kullanımı:

ClearDatabase<ApplicationDbContext>();

Bundan sonra DbContext'inizi yeniden başlatmayı unutmayın.


2

Aşağıdaki SQLite veritabanı üzerinde çalışır (Entity Framework kullanarak)

Tüm db tablolarını temizlemenin en hızlı yolu, yukarıdaki bazı yorumlar da vurgulandığı gibi, 'context.Database.ExecuteSqlCommand ("bazı SQL")' kullanmaktır. Burada tabloların 'index' sayısını nasıl sıfırlayacağımı göstereceğim.

            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 

Önemli bir nokta, tablolarınızda yabancı anahtarlar kullanırsanız, önce üst tablodan önce alt tabloyu silmeniz gerektiğidir, bu nedenle silme sırasında tabloların sırası (hiyerarşisi) önemlidir, aksi takdirde bir SQLite istisnası oluşabilir.

Not: var context = yeni YourContext ()


1

EF 5'te düzgün çalışır:

YourEntityModel myEntities = new YourEntityModel();

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

1

EFCore'da (kullandığım sürüm 3.1'dir) tüm satırları kaldırmak için aşağıdakileri kullanabilirsiniz -

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

0

Tüm kayıtları silin. Birincil dizini "kısalt" gibi sıfırlamayın.

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

neden silmek için tüm kayıtları çıkarsın ki? son derece verimsiz
kısrak

Çünkü performans benim önceliğim değildi. Modülerliğe dayanır, bu nedenle bir konum koşulu eklemek veya verileri kaldırmadan önce kontrol etmek isterseniz, bunu yapabilirsiniz. EF6, SQL I / O ile ilgili en yavaş araçtır, bu yüzden neden performans öncelik ise EF6 kullanmak soru olmalıdır ..
Roberto Mutti

0

Kodumda gerçekten Veritabanı nesnesine güzel bir erişim yoktu, bu yüzden her türlü sql kullanmak için izin verilen DbSet bunu yapabilirsiniz. Bir şekilde böyle bitecek:

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

0

MVC ise şunları yapabilirsiniz:

public async Task<IActionResult> DeleteAll()
{
    var list = await _context.YourClass.ToListAsync();
    _context.YourClass.RemoveRange(list);
    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));
}

0

Ebeveynleri silmeye çalıştığınızda tüm çocukların silme işlemi sırasında art arda olacağından emin olun. Ya da çocukların null edilebilir yabancı anahtarı vardır.


0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.