Khuôn khổ thực. Xóa tất cả các hàng trong bảng


280

Làm cách nào tôi có thể nhanh chóng xóa tất cả các hàng trong bảng bằng Entity Framework?

Tôi hiện đang sử dụng:

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

Tuy nhiên, phải mất một thời gian dài để thực hiện.

Có sự thay thế nào không?


22
Đọc câu trả lời tôi tự hỏi tại sao không ai trong số những người thích này TRUNCATElo lắng về các ràng buộc khóa ngoại.
Gert Arnold

2
Tôi rất ngạc nhiên về cách các câu trả lời ở đây chỉ chấp nhận rằng mọi người đều sử dụng Microsoft SQL Server, mặc dù sự hỗ trợ cho các cơ sở dữ liệu khác trong Entity Framework đã quay trở lại khi tôi có thể tìm thấy thông tin và chắc chắn có trước câu hỏi này trong vài năm . Mẹo: nếu một câu trả lời trích dẫn tên bảng trong các câu lệnh SQL có dấu ngoặc vuông (như [TableName]:), thì nó không khả dụng.
Đánh dấu Amery

Câu trả lời:


293

Đối với những người đang làm việc này và kết thúc ở đây như tôi, đây là cách bạn hiện đang làm trong EF5 và EF6:

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

Giả sử bối cảnh là một System.Data.Entity.DbContext


20
FYI, để sử dụng TRUNCATE, người dùng phải có quyền ALTER trên bàn. ( stackoverflow.com/questions/4735038/
Alex

7
@Alex Chỉ lãng phí rất nhiều thời gian cho lỗi "Không thể tìm thấy đối tượng MyTable vì nó không tồn tại hoặc bạn không có quyền." vì lý do chính xác đó - các quyền ALTER hiếm khi được cấp cho các ứng dụng EF và thông báo lỗi thực sự gửi cho bạn một cuộc rượt đuổi ngông cuồng.
Chris Moschini

8
Tôi đã có vấn đề vì bảng của tôi là một phần của mối quan hệ khóa ngoại, mặc dù đó là bảng lá trong mối quan hệ đó. Tôi cố gắng sử dụng bối cảnh.Database.ExecuteSqlCommand ("XÓA TỪ [Sở thích]"); thay vào đó
Dan Csharpster 8/8/2015

2
Lưu ý rằng mặc dù các [lối thoát ở đây là dành riêng cho SQL Server, nhưng TRUNCATElệnh này không phải là - đó là một phần của ANSI SQL và do đó sẽ hoạt động trong hầu hết các phương ngữ SQL (mặc dù không phải là SQLite).
Mark Amery

207

Cảnh báo: Phần sau chỉ phù hợp với các bảng nhỏ (nghĩ <1000 hàng)

Đây là một giải pháp sử dụng khung thực thể (không phải SQL) để xóa các hàng, vì vậy nó không phải là SQL Engine (R / DBM) cụ thể.

Điều này giả định rằng bạn đang làm điều này để thử nghiệm hoặc một số tình huống tương tự. Hoặc

  • Lượng dữ liệu nhỏ hoặc
  • Hiệu suất không thành vấn đề

Chỉ cần gọi:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

Giả sử bối cảnh này:

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

Đối với mã gọn hơn, bạn có thể khai báo phương thức mở rộng sau:

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

Sau đó, những điều trên trở thành:

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

Gần đây tôi đã sử dụng cách tiếp cận này để dọn sạch cơ sở dữ liệu thử nghiệm của mình cho mỗi lần chạy thử nghiệm (nó rõ ràng nhanh hơn so với việc tạo lại DB từ đầu mỗi lần, mặc dù tôi đã không kiểm tra hình thức của các lệnh xóa được tạo).


Tại sao nó có thể chậm?

  1. EF sẽ nhận được TẤT CẢ các hàng (VotesContext.Votes)
  2. và sau đó sẽ sử dụng ID của họ (không chắc chắn chính xác như thế nào, không quan trọng), để xóa chúng.

Vì vậy, nếu bạn đang làm việc với lượng dữ liệu nghiêm trọng, bạn sẽ giết quá trình máy chủ SQL (nó sẽ tiêu tốn toàn bộ bộ nhớ) và điều tương tự cho quy trình IIS vì EF sẽ lưu trữ tất cả dữ liệu giống như máy chủ SQL. Không sử dụng cái này nếu bảng của bạn chứa lượng dữ liệu nghiêm trọng.


1
Câu trả lời tuyệt vời, đã tăng tốc độ xóa tất cả các hàng của tôi theo hệ số 10! Lưu ý rằng tôi phải đổi tên phương thức mở rộng tĩnh Clear () thành phương thức như ClearDbSet () vì tôi đã có một phương thức mở rộng tĩnh Clear () khác được định nghĩa ở nơi khác trong dự án của tôi.
dodgy_coder 19/03/2015

1
@dodgy_coder đổi tên là không cần thiết vì lý do bạn đã đưa ra, vì phương thức mở rộng là dành cho DbSet, IDbSet chứ không phải IEnumerable, IList, ICollection, ICache hoặc bất kỳ giao diện nào khác mà "Clear" sẽ được yêu cầu. ưu tiên cho phương thức mở rộng là loại mà trên đó được xác định. nhưng nếu điều đó đọc rõ ràng hơn với bạn và không âm thanh dư thừa, Tuyệt vời!. Tôi, tôi rất vui vì nó giúp làm sáng tỏ! Chúc mừng!
Ahmed Alejo

Tôi rất vui vì bạn đã nêu chỉ cho các bảng nhỏ. Tôi hy vọng mọi người có được tầm quan trọng của lời giải thích của bạn. bởi vì cách này là cú pháp đường, cách thích hợp là cách Ron Sijm đề xuất. bởi vì bạn không tải dữ liệu trước khi xóa nó. cổ vũ mặc dù đã cho thấy và giải thích cách làm này.
yedevtxt

3
Điều này không thiết lập lại khóa nhận dạng. Vì vậy, nếu bạn xóa 10 bản ghi, bản tiếp theo sẽ vẫn là 11.
MDave

89

Sử dụng lệnh của SQL TRUNCATE TABLEsẽ nhanh nhất vì nó hoạt động trên bảng chứ không phải trên các hàng riêng lẻ.

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

Giả sử dataDblà một DbContext(không phải ObjectContext), bạn có thể gói nó và sử dụng phương thức như thế này:

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

Nếu bạn gặp lỗi quyền khi bạn thử điều này, chỉ cần thay đổi TRUNCATE TABLEthànhDELETE FROM
codeMonkey

1
@codeMonkey chỉ cần lưu ý rằng đây là các hoạt động khác nhau, nhưng sẽ có (gần như) hiệu ứng ròng giống nhau 👍
Rudi Visser

3
Nhưng XÓA không thiết lập lại hạt giống IDENTITY. Điều này có thể có vấn đề trong các tình huống nhất định.
Steve

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

11
Điều này không nên được sử dụng vì bạn thực hiện chọn đầy đủ và xóa sau thay vì chỉ xóa. Từ quan điểm hiệu suất thời gian này là một KHÔNG lớn!
HellBaby

2
@HellBaby Trừ khi nó hiếm khi được gọi và do đó hiệu suất khá không liên quan.
Alex

6
Ngay cả khi nó hiếm khi được gọi là điều này là xấu. Một bảng chỉ có 3000 mục có thể mất tới 30 giây do tốc độ theo dõi thay đổi của EF chậm như thế nào.
Nhẹ

1
Điều này chỉ phù hợp với các bảng nhỏ (<1000 hàng)
Amir Touitou

Hoàn hảo cho cơ sở dữ liệu trong bộ nhớ của tôi trong các bài kiểm tra đơn vị của tôi :)
SimonGates 16/12/19

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

hoặc là

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

1
nhưng khi tôi viết nó. "query.Delete ();" - "Xóa" không được nhận dạng
Zhenia

1
thêm tài liệu tham khảo của System.Data.Entity và EntityFrameWork trong dự án hiện tại của bạn
Manish Mishra

Phương pháp mở rộng nào là Xóa?
Ahmed Alejo

Theo như tôi biết thì không có phương pháp mở rộng Deletetrên IQueryable- Tôi đoán Manish đã sử dụng thứ gì đó như EntityFramework.Extends: github.com/loresoft/EntityFramework.Extends
null

Tôi đã chỉnh sửa câu trả lời của mình, trước đó nó đã gây hiểu nhầm. @null, bạn nói đúng, đây .Deletelà một tiện ích mở rộng tùy chỉnh và trong lúc nóng lòng đăng câu trả lời trước, hoàn toàn quên đề cập đến định nghĩa của tùy chỉnh này .Delete. :)
Manish Mishra

23

Bạn có thể làm điều đó mà không cần Foreach

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

Điều này sẽ loại bỏ tất cả các hàng


Nó sẽ cắt ngắn các mục tài sản điều hướng?
John Deer

19

Điều này tránh sử dụng bất kỳ sql

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

9

Tôi đã gặp câu hỏi này khi tôi phải giải quyết một trường hợp cụ thể: cập nhật đầy đủ nội dung trong bảng "lá" (không có FK nào chỉ vào nó). Điều này liên quan đến việc loại bỏ tất cả các hàng và đưa thông tin hàng mới và nó sẽ được thực hiện một cách giao dịch (tôi không muốn kết thúc với một bảng trống, nếu việc chèn không thành công vì bất kỳ lý do gì).

Tôi đã thử public static void Clear<T>(this DbSet<T> dbSet)cách tiếp cận, nhưng các hàng mới không được chèn. Một điều không hài lòng nữa là toàn bộ quá trình diễn ra chậm, vì các hàng bị xóa từng cái một.

Vì vậy, tôi đã chuyển sang TRUNCATEtiếp cận, vì nó nhanh hơn nhiều và nó cũng ROLLBACKable . Nó cũng đặt lại danh tính.

Ví dụ sử dụng mẫu kho lưu trữ:

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

Tất nhiên, như đã đề cập, giải pháp này không thể được sử dụng bởi các bảng được tham chiếu bởi các khóa ngoại (TRUNCATE không thành công).


Hai ý kiến: 1. Tên bảng nên được bọc trong [...]. Một trong các lớp / bảng của tôi được gọi là "Giao dịch", đó là một từ khóa SQL. 2. Nếu mục tiêu là xóa tất cả các bảng trong DB để kiểm tra đơn vị, các vấn đề với các ràng buộc khóa ngoài có thể dễ dàng được xử lý bằng cách ra lệnh cho các bảng để xử lý chúng theo cách các bảng con bị cắt trước các bảng cha.
Christoph

@Christoph - 1. Vâng, đó là sự thật. Tôi đã bỏ lỡ điều đó, bởi vì tôi luôn đặt tên bảng để tránh các từ khóa vì nó có thể dẫn đến rắc rối. 2. Nếu tôi nhớ chính xác, các bảng được tham chiếu bởi FK không thể bị cắt cụt (SQL Server ném Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), ngay cả khi chúng trống, do đó FK phải được loại bỏ và tạo lại để sử dụng TRUNCATE.
Alexei

5

nếu

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

nguyên nhân

Bảng rút gọn không thể 'MyTable' vì nó đang được tham chiếu bởi một ràng buộc FOREIGN KEY.

Tôi sử dụng cái này:

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

1
Nếu bạn có các ràng buộc khóa ngoài: (1) SQL có thể được đơn giản hóa thành "XÓA TỪ MyTable". (2) Điều này sẽ không đặt lại bộ đếm Id nếu bạn đã đặt nó ở chế độ tự động (cắt ngắn).
RGH

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

4

Nếu bạn muốn xóa toàn bộ cơ sở dữ liệu của bạn.

Do các ràng buộc của khóa ngoài, vấn đề là các bảng được cắt bớt. Đây là một cách để bruteforce trình tự này.

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

sử dụng:

ClearDatabase<ApplicationDbContext>();

hãy nhớ xác nhận lại DbContext của bạn sau này.


2

Các hoạt động sau đây trên cơ sở dữ liệu SQLite (sử dụng Entity Framework)

Có vẻ như cách nhanh nhất để xóa tất cả các bảng db là sử dụng 'bối cảnh.Database.ExecuteSqlCommand ("một số SQL")', như một số nhận xét ở trên cũng được nêu bật. Ở đây tôi cũng sẽ chỉ cho bạn cách thiết lập lại số lượng bảng 'chỉ mục'.

            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 

Một điểm quan trọng là nếu bạn sử dụng khóa ngoại trong các bảng của mình, trước tiên bạn phải xóa bảng con trước bảng cha, do đó trình tự (phân cấp) của các bảng trong khi xóa là quan trọng, nếu không có thể xảy ra ngoại lệ SQLite.

Lưu ý: var bối cảnh = new YourContext ()


1

Điều này hoạt động đúng trong EF 5:

YourEntityModel myEntities = new YourEntityModel();

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

1

Trong EFCore (phiên bản tôi đang sử dụng là 3.1), bạn có thể sử dụng cách sau để xóa tất cả các hàng -

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

0

Xóa tất cả hồ sơ. Không đặt lại chỉ mục chính như "cắt ngắ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;
}

Tại sao bạn lại rút tất cả các hồ sơ để xóa chúng? cực kỳ kém hiệu quả
mare

Bởi vì hiệu suất không phải là ưu tiên của tôi. Nó dựa trên tính mô-đun, vì vậy nếu bạn muốn thêm một điều kiện ở đâu hoặc kiểm tra dữ liệu trước khi xóa nó, bạn có thể. EF6 là công cụ chậm nhất về I / O SQL, vậy tại sao sử dụng EF6 nếu hiệu suất là ưu tiên nên là câu hỏi ..
Roberto Mutti

0

Trong mã của tôi, tôi không thực sự có quyền truy cập tốt vào đối tượng Cơ sở dữ liệu, vì vậy bạn có thể làm điều đó trên DbSet nơi bạn cũng được phép sử dụng bất kỳ loại sql nào. Nó sẽ kết thúc như thế này:

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

0

Nếu MVC, bạn có thể làm:

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

0

Hãy chắc chắn rằng khi bạn đang cố gắng xóa cha mẹ, tất cả trẻ em sẽ xếp tầng xóa. Hoặc trẻ em có khóa ngoại không có giá trị.


0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.