实体框架。删除表中的所有行


280

如何使用Entity Framework快速删除表中的所有行?

我目前正在使用:

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

但是,执行需要很长时间。

还有其他选择吗?


22
阅读答案,我想知道为什么这些专家都不TRUNCATE担心外键约束。
Gert Arnold 2014年

2
我对每个人都在使用Microsoft SQL Server的答案是理所当然的感到惊讶,即使在Entity Framework中对其他数据库的支持可以追溯到我可以找到有关该问题的信息之前,也早于几年之前。提示:如果答案在SQL语句中用方括号括起来的表名(例如:),则该表名[TableName]不可移植。
Mark Amery

Answers:


293

对于那些正在使用谷歌搜索并最终像我这样的人,这是您目前在EF5和EF6中的操作方式:

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

假设上下文是一个 System.Data.Entity.DbContext


20
仅供参考,为了使用TRUNCATE,用户必须对表具有ALTER权限。(stackoverflow.com/questions/4735038/...
亚历

7
@Alex在错误“找不到对象MyTable,因为它不存在或您没有权限”上浪费了很多时间。出于这个确切原因-很少将TER应用程序的权限授予EF应用程序,并且错误消息确实使您大吃一惊。
克里斯·莫斯基尼

8
由于表是外键关系的一部分,所以我遇到了问题,即使它是该关系中的叶表。我最后使用context.Database.ExecuteSqlCommand(“ DELETE FROM [Interests]”); 相反
Dan Csharpster

2
请注意,尽管[此处的转义特定于SQL Server,但TRUNCATE命令不是-它是ANSI SQL的一部分,因此可以在大多数SQL方言中使用(尽管不是SQLite)。
Mark Amery

207

警告:以下内容仅适用于小型表(认为<1000行)

这是一个使用实体框架(不是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;}
}

对于整理代码,您可以声明以下扩展方法:

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

最近,我使用了这种方法来为每个测试用例运行清理我的测试数据库(显然,这比每次从头开始重新创建数据库都快,尽管我没有检查生成的delete命令的形式)。


为什么会变慢?

  1. EF将获得所有行(VotingContext.Votes)
  2. 然后使用它们的ID(不确定确切如何,没关系)来删除它们。

因此,如果您要处理大量数据,则会杀死SQL Server进程(它将消耗所有内存),并且会破坏IIS进程,因为EF将以与SQL Server相同的方式缓存所有数据。如果您的表中包含大量数据,请不要使用此选项。


1
很好的答案,我删除所有行代码的速度提高了10倍!请注意,由于我已经在项目的其他位置定义了另一个Clear()静态扩展方法,因此必须将Clear()静态扩展方法重命名为类似ClearDbSet()的名称。
dodgy_coder 2015年

1
由于您给出的原因,@ dodgy_coder重命名不是必需的,因为扩展方法用于DbSet,IDbSet而不是IEnumerable,IList,ICollection,ICache或任何其他需要“清除”的接口。扩展方法的首选项是定义的类型。但是,如果您看得更清楚,而且听起来不太多余,那就太好了!我很高兴能为您带来明智的表现!干杯!
艾哈迈德·阿莱霍

我很高兴您只为小桌发言。我希望人们理解您的解释的重要性。因为这种方法是语法糖,所以正确的方法是Ron Sijm建议的方法。因为您在删除数据之前不会加载数据。尽管为展示和解释这种方式而欢呼。
yedevtxt '18

3
这不会重置身份密钥。因此,如果您清除10条记录,则下一条仍将是11条
。– MDave

89

使用SQL的TRUNCATE TABLE命令将是最快的,因为它在表上而不是对单个行进行操作。

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

假设dataDbDbContext(而不是ObjectContext),则可以将其包装并使用如下方法:

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

如果尝试此操作时遇到权限错误,请更改TRUNCATE TABLEDELETE FROM
codeMonkey

1
@codeMonkey只要注意一下,这些都是不同的操作,但将有(几乎)相同的净效应👍
鲁迪·维瑟

2
但是DELETE不会重置IDENTITY种子。在某些情况下这可能会出现问题。
史蒂夫

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

11
不应使用此选项,因为您将执行完全选择和之后的删除,而不仅仅是删除。从时间性能角度来看,这不是一个大问题!
HellBaby 2014年

2
@HellBaby除非它很少被调用,所以其性能是无关紧要的。
Alex

6
即使它很少被称为这是不好的。由于EF更改跟踪的速度很慢,只有3000个条目的表可能需要30秒钟以上的时间。
2016年

1
这仅适用于小桌子(<1000行)
Amir Touitou

在单元测试中非常适合我的内存数据库:)
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);
}

要么

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

1
但是当我写它时。“ query.Delete();” -无法识别“删除”
Zhenia

1
在当前项目中添加System.Data.Entity和EntityFrameWork的引用
Manish Mishra 2013年

删除的扩展方式是什么?
艾哈迈德·阿莱霍

据我所知,没有一个扩展方法DeleteIQueryable-我猜马尼什使用类似EntityFramework.Extended:github.com/loresoft/EntityFramework.Extended

我已经编辑了答案,之前有误导性。@null,您是对的,这.Delete是一个自定义扩展名,在首先发布答案的过程中,完全忘了提及此自定义的定义了.Delete。:)
Manish Mishra


19

这样可以避免使用任何SQL

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

9

当我不得不处理一个特殊情况时,我遇到了这个问题:完全更新“叶”表中的内容(没有FK指向它)。这涉及删除所有行并放入新行信息,并且应该以事务方式完成(如果插入由于某种原因而失败,我不想以空表结尾)。

我尝试了该public static void Clear<T>(this DbSet<T> dbSet)方法,但是未插入新行。另一个缺点是整个过程很慢,因为行被逐行删除。

因此,我改用了TRUNCATE方法,因为它快得多而且也可以ROLLBACKable。它还会重置身份。

使用存储库模式的示例:

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引用的表Cannot truncate table because it is being referenced by a FOREIGN KEY constraint为空,它们也不能被截断(SQL Server抛出),因此无论如何都必须删除并重新创建 FK 才能使用TRUNCATE
阿列克谢

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
如果您有外键约束:(1)SQL可以简化为“从MyTable删除”。(2)如果将ID计数器设置为自动递增,则不会重置ID计数器(truncate可以)。
RGH

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

4

如果您希望清除整个数据库。

由于外键约束,表被截断的顺序很重要。这是暴力破解此序列的一种方法。

    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数据库(使用实体框架)

似乎清除所有数据库表的最快方法是使用'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;
}

为什么要拔出所有记录以将其删除?效率极低
母马

因为性能不是我的首要任务。它基于模块化,因此,如果要添加where条件或在删除数据之前检查数据,则可以。EF6是关于SQL I / O,那么为什么使用EF6如果性能是应该优先的问题..最慢的工具
罗伯托Mutti

0

在我的代码中,我实际上并没有很好地访问Database对象,因此您可以在DbSet上进行操作,在该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
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.