【问题标题】:Entity Framework. Delete all rows in table实体框架。删除表中的所有行
【发布时间】:2013-02-19 15:41:44
【问题描述】:

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

我目前正在使用:

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

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

还有其他选择吗?

【问题讨论】:

  • 阅读答案我想知道为什么这些TRUNCATE 专家都不担心外键约束。
  • 我有点惊讶于这里的答案如何理所当然地认为每个人都在使用 Microsoft SQL Server,即使在 Entity Framework 中对其他数据库的支持可以追溯到我能找到的信息并且肯定比这个问题早了几年。提示:如果答案在 SQL 语句中用方括号引用表名(例如:[TableName]),则它不可移植。
  • 你有没有想过“不使用 ORM”是一个答案? ORM 有很多用途——批量操作不是其中之一。删除所有行不涉及业务逻辑,这就是 ORM 的亮点。

标签: c# sql linq entity-framework


【解决方案1】:

对于那些像我一样在谷歌上搜索并最终来到这里的人,这就是您目前在 EF5 和 EF6 中的操作方式:

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

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

【讨论】:

  • 仅供参考,为了使用 TRUNCATE,用户必须对表具有 ALTER 权限。 (stackoverflow.com/questions/4735038/…)
  • @Alex 刚刚在错误“找不到对象 MyTable 因为它不存在或您没有权限”上浪费了大量时间。正是因为这个原因 - 很少向 EF 应用程序授予 ALTER 权限,并且错误消息确实让您大吃一惊。
  • 我遇到了问题,因为我的表是外键关系的一部分,即使它是该关系中的叶表。我结束了使用 context.Database.ExecuteSqlCommand("DELETE FROM [Interests]");而是
  • 请注意,虽然这里的 [ 转义是特定于 SQL Server 的,但 TRUNCATE 命令不是 - 它是 ANSI SQL 的一部分,因此适用于大多数 SQL 方言(尽管不是 SQLite)。
【解决方案2】:

警告:以下仅适用于小表(认为

这是一个使用实体框架(不是 SQL)来删除行的解决方案,因此它不是特定于 SQL 引擎(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();

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


为什么会慢?

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

因此,如果您正在处理大量数据,您将终止 SQL Server 进程(它将消耗所有内存)和 IIS 进程同样的事情,因为 EF 将以与 SQL Server 相同的方式缓存所有数据。如果您的表包含大量数据,请不要使用这个。

【讨论】:

  • 很好的答案,将我的删除所有行代码加快了 10 倍!请注意,我必须将 Clear() 静态扩展方法重命名为 ClearDbSet() 之类的名称,因为我已经在项目的其他地方定义了另一个 Clear() 静态扩展方法。
  • @dodgy_coder 重命名不是必要的,因为您给出的原因,因为扩展方法适用于 DbSet、IDbSet 而不是 IEnumerable、IList、ICollection、ICache 或任何其他需要“清除”的接口.扩展方法的偏好是定义的类型。但是,如果这对您来说更清楚并且听起来不多余,那就太好了!我很高兴它有助于提高性能!干杯!
  • 我很高兴您只针对小桌子声明。我希望人们明白你解释的重要性。因为这种方式是语法糖,所以正确的方式是 Ron Sijm 建议的方式。因为您在删除数据之前不加载数据。为展示和解释这种做法而欢呼。
  • 这不会重置身份密钥。所以如果你清除了 10 条记录,下一条仍然是 11 条。
  • 我必须在RemoveRange之后显式调用_context.SaveChanges()
【解决方案3】:

使用 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 TABLE 更改为 DELETE FROM
  • @codeMonkey 请注意,这些是不同的操作,但会产生(几乎)相同的净效果?
  • 但 DELETE 不会重置 IDENTITY 种子。这在某些情况下可能会出现问题。
【解决方案4】:
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();

【讨论】:

  • 这不应该被使用,因为你执行一个完整的选择和一个删除之后,而不是仅仅一个删除。从时间性能的角度来看,这是一个很大的错误!
  • @HellBaby 除非它很少被调用,因此性能无关紧要。
  • 即使它很少被称为这是不好的。由于 EF 更改跟踪的速度很慢,一个只有 3000 个条目的表可能需要 30 秒以上的时间。
  • 这个只适用于小表(
  • 在我的单元测试中非常适合我的内存数据库:)
【解决方案5】:
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]");
}

【讨论】:

  • 但是当我写它的时候。"query.Delete();" - “删除”无法识别
  • 在当前项目中添加 System.Data.Entity 和 EntityFrameWork 的引用
  • Delete是什么扩展方式?
  • 据我所知,IQueryable 上没有扩展方法 Delete -- 我猜 Manish 正在使用类似 EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
  • 我已经编辑了我的答案,之前它具有误导性。 @null,你说得对,这个.Delete 是一个自定义扩展,在第一时间发布答案的时候,完全忘记了这个自定义.Delete 的定义。 :)
【解决方案6】:

不用 Foreach 也可以做到这一点

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

这将删除所有行

【讨论】:

  • 它会截断导航属性项吗?
  • 仅适用于小桌子
【解决方案7】:

这样可以避免使用任何 sql

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

【讨论】:

【解决方案8】:

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

我尝试了public static void Clear&lt;T&gt;(this DbSet&lt;T&gt; 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 失败)。

【讨论】:

  • 两个 cmets: 1. 表名应该包含在 [...] 中。我的一个类/表称为“事务”,它是一个 SQL 关键字。 2. 如果目标是清除数据库中的所有表以进行单元测试,则可以通过命令表以在父表之前被截断的方式对表进行处理,从而轻松解决外键约束问题。
  • @Christoph - 1. 是的,这是真的。我错过了,因为我总是命名表以避免关键字,因为它可能会导致麻烦。 2.如果我没记错的话,FKs引用的表不能被截断(SQL Server抛出Cannot truncate table because it is being referenced by a FOREIGN KEY constraint),即使它们是空的,所以FKs必须是dropped and recreated才能使用TRUNCATE
【解决方案9】:
context.TableName.RemoveRange(context.TableName);
context.SaveChanges();

【讨论】:

  • @mahen3d 哪个链接?
【解决方案10】:

如果

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) SQL 可以简化为“DELETE FROM MyTable”。 (2) 如果您将 Id 计数器设置为自动递增(截断),这不会重置 ID 计数器。
【解决方案11】:
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();

【讨论】:

    【解决方案12】:

    如果您想清除整个数据库。

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

        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。

    【讨论】:

      【解决方案13】:

      以下适用于 SQLite 数据库(使用实体框架)。

      似乎清除所有数据库表的最快方法是使用context.Database.ExecuteSqlCommand("some SQL"),因为上面的一些 cmets 也突出显示。在这里,我还将展示如何重置表的“索引”计数。

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

      【讨论】:

        【解决方案14】:

        在 EFCore(我使用的版本是 3.1)中,您可以使用以下内容删除所有行 -

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

        【讨论】:

          【解决方案15】:

          这在 EF 5 中可以正常工作:

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

          【讨论】:

            【解决方案16】:

            这对我有用... EF v3.1.5

            context.ModelName.RemoveRange(context.ModelName.ToList());
            context.SaveChanges();
            

            【讨论】:

              【解决方案17】:

              删除所有记录。不要像“截断”那样重置主索引。

              /// <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 应该是个问题..
              【解决方案18】:

              在我的代码中,我并没有很好地访问数据库对象,因此您可以在 DbSet 上执行此操作,您也可以使用任何类型的 sql。它会像这样结束:

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

              【讨论】:

                【解决方案19】:

                如果是 MVC,你可以这样做:

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

                【讨论】:

                  【解决方案20】:

                  确保当您尝试删除父级时,所有子级都会在删除时级联。或者孩子有可以为空的外键。

                  【讨论】:

                    【解决方案21】:
                    var list = db.Discounts.ToList().Select(x => x as Discount);
                    foreach (var item in list)
                    {
                        db.Discounts.Remove(item);
                    }
                    db.SaveChanges();
                    

                    【讨论】:

                      【解决方案22】:

                      这是 Ron 对流行的 solution 的一个变体,它利用另一个流行的堆栈溢出解决方案来确定实体框架类的基础表名,从而避免使用硬编码字符串表名。

                      使用这些扩展方法,解决方案如下所示:

                      _dbContext.TruncateTable<TheTableName>();
                      

                      (如果您在 EF DBContext 类或部分类文件中编辑代码,请使用 this.TruncateTable&lt;...

                      这是扩展类:

                      public static class EntityFrameworkExtensions
                      {
                          private static string ParseTableNameFromSQL(string sql)
                          {
                              Regex regex = new Regex("FROM (?<table>.*) AS");
                              Match match = regex.Match(sql);
                      
                              string table = match.Groups["table"].Value;
                              return table;
                          }
                          
                          public static string GetTableName<T>(this IObjectContextAdapter context) where T : class =>
                              ParseTableNameFromSQL(context.ObjectContext.CreateObjectSet<T>().ToTraceString());
                          
                          public static void TruncateTable<T>(this DbContext dbContext) where T : class =>
                              dbContext.Database.ExecuteSqlCommand($"TRUNCATE TABLE {dbContext.GetTableName<T>()}");
                      
                          public static void DeleteAllTableRows<T>(this DbContext dbContext) where T : class =>
                              dbContext.Database.ExecuteSqlCommand($"DELETE FROM {dbContext.GetTableName<T>()}");
                      }
                      
                      

                      如果您的表不能被截断(例如由于外键引用),最后一个扩展方法 DeleteAllTableRows 是一个有用的替代方法 - 这仍然比实体框架RemoveAll 替代方法快得多。

                      【讨论】:

                      • 又一个截断表的答案:(。几乎不可能截断表。
                      • @GertArnold 只是为了你,我还添加了一个 DELETE FROM 扩展名(事实上,在许多情况下我经常不得不依赖它)
                      【解决方案23】:

                      适用于 EF Core 3

                      public static class EntityExtensions
                      {
                          public static async Task ClearAsync<T>(this DbSet<T> dbSet) where T : class
                          {
                              var command = dbSet.CreateDbCommand();
                              command.CommandText = $"TRUNCATE TABLE {dbSet.EntityType.GetSchema()}.{dbSet.EntityType.GetTableName()}";
                              await command.ExecuteNonQueryAsync();
                          }
                      }
                      

                      但请注意 dbSet.CreateDbCommand 是一个扩展

                      【讨论】:

                      • 不错!但是,你为什么不提供扩展呢!
                      【解决方案24】:

                      这里几乎所有的答案都有几个问题:

                      1] 硬编码的 sql。括号是否适用于所有数据库引擎?
                      2] 实体框架RemoveRemoveRange 调用。这会将所有实体加载到受操作影响的内存中。哎呀。
                      3] 截断表。与外键引用中断,可能不适用于所有数据库引擎。

                      使用https://entityframework-plus.net/,他们处理跨数据库平台的东西,将delete翻译成正确的sql语句并且不将实体加载到内存中,并且该库是免费和开源的。

                      免责声明:我不隶属于 nuget 包。他们确实提供了功能更多的付费版本。

                      【讨论】:

                      • 使用第三方库(非绝对必要时)也是一个问题。它们改变了,它们被弃用了,它们变成了付费库等等。我宁愿使用 SQL。
                      • 当然。您可以随时拍摄第 3 方库的快照并按原样集成,这样就不会损坏。
                      • 但是,即使您直接引用 nuget 包,这也是人们编写集成测试的原因;)但每个人都有自己的。
                      猜你喜欢
                      • 2011-08-30
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-07-04
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多