Entity Framework ลบแถวทั้งหมดในตาราง


280

ฉันจะลบแถวทั้งหมดในตารางอย่างรวดเร็วโดยใช้ Entity Framework ได้อย่างไร

ฉันกำลังใช้:

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

อย่างไรก็ตามมันใช้เวลานานในการดำเนินการ

มีทางเลือกอื่นอีกไหม?


22
การอ่านคำตอบฉันสงสัยว่าเหตุใดจึงไม่มีTRUNCATEadepts เหล่านี้กังวลเกี่ยวกับข้อ จำกัด ของรหัสต่างประเทศ
Gert Arnold

2
ฉันรู้สึกประหลาดใจกับคำตอบที่ได้รับจากการที่ทุกคนใช้ Microsoft SQL Server แม้ว่าการสนับสนุนฐานข้อมูลอื่นใน Entity Framework จะกลับไปไกลเท่าที่ฉันสามารถหาข้อมูลเกี่ยวกับคำถามนี้มาก่อนได้หลายปี . เคล็ดลับ: หากคำตอบพูดชื่อตารางในคำสั่ง SQL ที่มีเครื่องหมายวงเล็บแบบสี่เหลี่ยม (เช่น:) แสดง[TableName]ว่าไม่ใช่แบบพกพา
Mark Amery

คำตอบ:


293

สำหรับผู้ที่ googling และสิ้นสุดที่นี่เช่นฉันนี่คือวิธีที่คุณทำใน EF5 และ EF6:

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

สมมติว่าบริบทเป็น System.Data.Entity.DbContext


20
FYI ในการใช้ TRUNCATE ผู้ใช้จะต้องได้รับอนุญาตจาก ALTER บนโต๊ะ ( stackoverflow.com/questions/4735038/… )
Alex

7
@Alex เสียเวลาไปกับข้อผิดพลาด "ไม่สามารถหาวัตถุ MyTable เพราะมันไม่มีอยู่หรือคุณไม่มีสิทธิ์" ด้วยเหตุผลที่แน่นอน - การอนุญาต ALTER จะไม่ค่อยได้รับสิทธิ์ให้กับแอพ EF และข้อความแสดงข้อผิดพลาดจะส่งคุณในการไล่ล่าห่านอย่างแท้จริง
Chris Moschini

8
ฉันมีปัญหาเนื่องจากตารางของฉันเป็นส่วนหนึ่งของความสัมพันธ์ที่สำคัญกับต่างประเทศแม้ว่าจะเป็นตารางใบไม้ในความสัมพันธ์นั้น ฉันปิดบังโดยใช้ context.Database.ExecuteSqlCommand ("ลบจาก [ความสนใจ]"); แทน
Dan Csharpster

2
โปรดทราบว่าในขณะที่การ[ยกเว้นที่นี่มีเฉพาะกับ SQL Server TRUNCATEคำสั่งไม่ได้เป็นส่วนหนึ่งของ ANSI SQL และจะทำงานในภาษา SQL ส่วนใหญ่ (แม้ว่าจะไม่ใช่ SQLite)
Mark Amery

207

คำเตือน:ต่อไปนี้เหมาะสำหรับตารางเล็ก ๆ เท่านั้น (คิดว่า <1,000 แถว)

นี่คือโซลูชันที่ใช้เฟรมเวิร์กเอนทิตี (ไม่ใช่ SQL) เพื่อลบแถวดังนั้นจึงไม่ใช่เฉพาะ SQL Engine (R / DBM)

สมมติว่าคุณกำลังทำสิ่งนี้เพื่อการทดสอบหรือในบางสถานการณ์ที่คล้ายกัน ทั้ง

  • ปริมาณข้อมูลมีขนาดเล็กหรือ
  • ประสิทธิภาพไม่สำคัญ

เพียงโทร:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

สมมติว่าบริบทนี้:

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

สำหรับรหัส tidier คุณสามารถประกาศวิธีการต่อไปนี้:

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

จากนั้นจะกลายเป็นข้างต้น:

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

ฉันเพิ่งใช้วิธีนี้เพื่อล้างฐานข้อมูลการทดสอบของฉันสำหรับการทดสอบแต่ละครั้ง (เร็วกว่าการสร้างฐานข้อมูลซ้ำในแต่ละครั้งแม้ว่าจะไม่ได้ตรวจสอบรูปแบบของคำสั่งการลบที่สร้างขึ้น)


ทำไมช้า?

  1. EF จะได้แถวทั้งหมด (VotingContext.Votes)
  2. จากนั้นจะใช้ ID ของพวกเขา (ไม่แน่ใจว่าจะทำอย่างไรไม่สำคัญ) ในการลบพวกเขา

ดังนั้นหากคุณทำงานกับข้อมูลจำนวนมากคุณจะฆ่ากระบวนการเซิร์ฟเวอร์ SQL (มันจะใช้หน่วยความจำทั้งหมด) และสิ่งเดียวกันสำหรับกระบวนการ IIS เนื่องจาก EF จะแคชข้อมูลทั้งหมดเช่นเดียวกับเซิร์ฟเวอร์ SQL อย่าใช้อันนี้หากตารางของคุณมีข้อมูลจำนวนมาก


1
คำตอบที่ดีเร่งความเร็วให้ฉันลบโค้ดแถวทั้งหมดด้วยปัจจัย 10! โปรดทราบว่าฉันต้องเปลี่ยนชื่อวิธีส่วนขยายแบบคงที่ Clear () เป็น ClearDbSet () เนื่องจากฉันมีวิธีส่วนขยายแบบคงที่ Clear () แบบอื่นที่กำหนดไว้ที่อื่นในโครงการของฉัน
dodgy_coder

1
@dodgy_coder การเปลี่ยนชื่อไม่จำเป็นสำหรับเหตุผลที่คุณให้เนื่องจากวิธีการขยายใช้สำหรับ DbSet, IDbSet s และไม่ใช่ IEnumerable, IList, ICollection, ICache หรืออินเทอร์เฟซอื่น ๆ ที่จำเป็นต้องมี "การล้าง" การตั้งค่าสำหรับวิธีการขยายเป็นประเภทที่มีการกำหนดไว้ แต่ถ้าสิ่งนั้นอ่านชัดกว่าสำหรับคุณและไม่ได้ฟังซ้ำซ้อน Great! ฉันดีใจที่มันช่วยให้คนฉลาดขึ้น! ไชโย!
Ahmed Alejo

ฉันดีใจที่คุณระบุไว้ในตารางเล็ก ๆ เท่านั้น ฉันหวังว่าผู้คนจะให้ความสำคัญกับคำอธิบายของคุณ เพราะวิธีนี้เป็นน้ำตาลเชิงซ้อนวิธีที่เหมาะสมคือวิธีที่ Ron Sijm แนะนำ เพราะคุณไม่โหลดข้อมูลก่อนที่จะลบ ไชโยแม้ว่าจะแสดงและอธิบายวิธีการทำเช่นนี้
yedevtxt

3
สิ่งนี้ไม่ได้รีเซ็ตรหัสประจำตัว ดังนั้นถ้าคุณล้างระเบียน 10 ระเบียนรายการถัดไปจะยังคงเป็น 11
MDave

89

การใช้TRUNCATE TABLEคำสั่งของ SQL จะเร็วที่สุดในขณะที่ทำงานบนตารางและไม่ได้อยู่ในแต่ละแถว

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

สมมติว่าdataDbเป็นDbContext(ไม่ใช่ObjectContext) คุณสามารถปิดและใช้วิธีการเช่นนี้:

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

หากคุณได้รับข้อผิดพลาดเกี่ยวกับการอนุญาตเมื่อคุณลองทำสิ่งนี้เพียงแค่เปลี่ยนTRUNCATE TABLEเป็นDELETE FROM
codeMonkey

1
@codeMonkey เพิ่งทราบว่าสิ่งเหล่านี้เป็นการดำเนินการที่แตกต่างกัน แต่จะมีผลกระทบสุทธิเดียวกัน (เกือบ) 👍
Rudi Visser

3
แต่ลบไม่ได้รีเซ็ตเมล็ดข้อมูลประจำตัว นี่อาจเป็นปัญหาในบางสถานการณ์
สตีฟ

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

11
สิ่งนี้ไม่ควรใช้เนื่องจากคุณทำการเลือกแบบเต็มและลบหลังจากแทนที่จะเป็นเพียงการลบ จากมุมมองประสิทธิภาพเวลานี่ไม่ใช่เรื่องใหญ่!
HellBaby

2
@HellBaby เว้นแต่ว่ามันจะไม่ค่อยได้รับการเรียกและทำให้การแสดงค่อนข้างไม่เกี่ยวข้อง
อเล็กซ์

6
แม้ว่ามันจะไม่ค่อยเรียกว่ามันแย่ก็ตาม ตารางที่มีเพียง 3,000 รายการอาจใช้เวลามากกว่า 30 วินาทีเนื่องจากการติดตามการเปลี่ยนแปลงของ EF ที่ช้า
Slight

1
เหมาะสำหรับโต๊ะเล็ก (<1,000 แถว)
Amir Touitou

สมบูรณ์แบบสำหรับฐานข้อมูลในหน่วยความจำของฉันในการทดสอบหน่วยของฉัน :)
38432

38
using (var context = new DataDb())
{
     var ctx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext;
     ctx.ExecuteStoreCommand("DELETE FROM [TableName] WHERE Name= {0}", Name);
}

หรือ

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

1
แต่เมื่อฉันเขียนมัน "query.Delete ();" - "ลบ" ไม่รู้จัก
Zhenia

1
เพิ่มการอ้างอิงของ System.Data.Entity และ EntityFrameWork ในโครงการปัจจุบันของคุณ
Manish Mishra

วิธีการขยายคือลบอะไร
Ahmed Alejo

เท่าที่ผมรู้ว่ามีไม่ได้เป็นวิธีขยายDeleteบนIQueryable- ฉันคาดเดา Manish ถูกใช้สิ่งที่ต้องการ EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
null

ฉันได้แก้ไขคำตอบก่อนหน้านี้มันทำให้เข้าใจผิด @ เปล่าคุณพูดถูกนี่.Deleteเป็นส่วนขยายที่กำหนดเองและในความร้อนของการโพสต์คำตอบก่อนลืมทั้งหมดที่พูดถึงคำจำกัดความของประเพณี.Deleteนี้ :)
Manish Mishra

23

คุณสามารถทำได้โดยไม่มี Foreach

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

จะเป็นการลบแถวทั้งหมด


มันจะตัดทอนรายการคุณสมบัติการนำทางหรือไม่
John Deer

19

หลีกเลี่ยงการใช้ sql ใด ๆ

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

9

ฉันเจอคำถามนี้เมื่อฉันต้องจัดการกับกรณีเฉพาะ: อัปเดตเนื้อหาในตาราง "leaf" อย่างเต็มที่ (ไม่มี FK ที่ชี้ไปที่มัน) สิ่งนี้เกี่ยวข้องกับการลบแถวทั้งหมดและวางข้อมูลแถวใหม่และควรทำธุรกรรม (ฉันไม่ต้องการที่จะจบลงด้วยตารางว่างเปล่าหากส่วนแทรกล้มเหลวด้วยเหตุผลใดก็ตาม)

ฉันได้ลอง public static void Clear<T>(this DbSet<T> dbSet)วิธีนี้แล้ว แต่ยังไม่ได้แทรกแถวใหม่ ข้อเสียอีกประการหนึ่งคือกระบวนการทั้งหมดช้าเนื่องจากแถวถูกลบทีละรายการ

ดังนั้นฉันจึงเปลี่ยนมาTRUNCATEใช้วิธีนี้เนื่องจากเร็วกว่ามากและสามารถย้อนกลับได้ นอกจากนี้ยังรีเซ็ตตัวตน

ตัวอย่างการใช้รูปแบบพื้นที่เก็บข้อมูล:

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

แน่นอนดังที่ได้กล่าวไปแล้วไม่สามารถใช้วิธีแก้ปัญหานี้โดยตารางที่อ้างอิงโดยคีย์ต่างประเทศ (TRUNCATE ล้มเหลว)


สองความคิดเห็น: 1. ชื่อตารางควรถูกห่อใน [... ] หนึ่งในคลาส / ตารางของฉันเรียกว่า "ธุรกรรม" ซึ่งเป็นคำหลัก SQL 2. หากเป้าหมายคือการล้างตารางทั้งหมดในฐานข้อมูลสำหรับการทดสอบหน่วยปัญหาเกี่ยวกับข้อ จำกัด ของคีย์ต่างประเทศสามารถแก้ไขได้โดยง่ายโดยสั่งให้ตารางประมวลผลตารางในลักษณะที่ตารางลูกจะถูกตัดทอนก่อนตารางหลัก
Christoph

@Christoph - 1. ใช่นั่นเป็นเรื่องจริง ฉันพลาดไปเพราะฉันตั้งชื่อตารางเพื่อหลีกเลี่ยงคำหลักเนื่องจากอาจทำให้เกิดปัญหา 2. ถ้าฉันจำได้อย่างถูกต้องตารางที่อ้างอิงโดย FK จะไม่สามารถถูกตัดทอนได้ (SQL Server พ่นCannot truncate table because it is being referenced by a FOREIGN KEY constraint) แม้ว่าตารางเหล่านั้นจะว่างเปล่าดังนั้น FK ต้องถูกลบและสร้างใหม่เพื่อใช้งานTRUNCATEต่อไป
Alexei

5

ถ้า

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

สาเหตุ

ไม่สามารถตัดทอนตาราง 'MyTable' ได้เนื่องจากมีการอ้างอิงโดยข้อ จำกัด ของ FOREIGN KEY

ฉันใช้สิ่งนี้:

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

1
หากคุณมีข้อ จำกัด ของ foreign key: (1) SQL สามารถทำให้ง่ายขึ้นเป็น "DELETE FROM MyTable" (2) การทำเช่นนี้จะไม่รีเซ็ตตัวนับ Id หากคุณตั้งค่าเป็น autoincrement (ตัดออก)
RGH


4

หากคุณต้องการล้างฐานข้อมูลทั้งหมดของคุณ

เนื่องจากข้อ จำกัด foreign-key มันสำคัญที่ลำดับตารางถูกตัดทอน นี่เป็นวิธีการที่จะทำให้ลำดับนี้เป็นจริง

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

การใช้งาน:

ClearDatabase<ApplicationDbContext>();

อย่าลืมเตรียม DbContext ของคุณใหม่หลังจากนี้


2

การทำงานต่อไปนี้ในฐานข้อมูล SQLite (โดยใช้ Entity Framework)

ดูเหมือนว่าวิธีที่เร็วที่สุดในการล้างตาราง db ทั้งหมดนั้นใช้ 'context.Database.ExecuteSqlCommand ("some SQL")' เนื่องจากความคิดเห็นบางส่วนถูกเน้นไว้เช่นกัน ที่นี่ฉันจะแสดงวิธีการรีเซ็ตการนับ 'ดัชนี' ของตารางด้วย

            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 

จุดสำคัญอย่างหนึ่งคือถ้าคุณใช้คีย์ต่างประเทศในตารางของคุณคุณต้องลบตารางลูกก่อนตารางหลักดังนั้นลำดับ (ลำดับชั้น) ของตารางระหว่างการลบนั้นเป็นสิ่งที่สำคัญมิฉะนั้นอาจเกิดข้อยกเว้น SQLite ขึ้น

หมายเหตุ: var context = new YourContext ()


1

สิ่งนี้ทำงานได้อย่างถูกต้องใน EF 5:

YourEntityModel myEntities = new YourEntityModel();

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

1

ใน EFCore (เวอร์ชั่นที่ฉันใช้คือ 3.1) คุณสามารถใช้รายการต่อไปนี้เพื่อลบแถวทั้งหมด -

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

0

ลบระเบียนทั้งหมด อย่ารีเซ็ตดัชนีหลักเช่น "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;
}

เหตุใดคุณจึงต้องดึงระเบียนทั้งหมดออกเพื่อลบออก ไม่มีประสิทธิภาพมาก
เมีย

เพราะการแสดงไม่ใช่ความสำคัญของฉัน มันขึ้นอยู่กับ modularity ดังนั้นหากคุณต้องการเพิ่มเงื่อนไข Where หรือตรวจสอบข้อมูลก่อนที่จะลบคุณสามารถ EF6 เป็นเครื่องมือที่ช้าที่สุดเกี่ยวกับ SQL I / O ดังนั้นทำไมใช้ EF6 ว่าประสิทธิภาพการทำงานเป็นลำดับความสำคัญที่ควรจะเป็นคำถามที่ ..
โรแบร์โต Mutti

0

ในรหัสของฉันฉันไม่ได้มีการเข้าถึงวัตถุฐานข้อมูลที่ดีจริงๆดังนั้นคุณสามารถทำได้บน DbSet ที่คุณได้รับอนุญาตให้ใช้ sql ชนิดใดก็ได้ มันจะจบแบบนี้:

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

0

ถ้า MVC คุณสามารถทำได้:

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

0

ตรวจสอบให้แน่ใจเมื่อคุณพยายามที่จะลบผู้ปกครองเด็ก ๆ ทุกคนจะเรียงซ้อนในการลบ หรือเด็กมีคีย์ต่างประเทศ nullable


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.