【问题标题】:Delete all entities in Entity Framework删除实体框架中的所有实体
【发布时间】:2011-08-30 16:06:03
【问题描述】:

我想使用 Entity Framework 4+ 删除所有表(所有实体)的内容。如何做到这一点?

【问题讨论】:

    标签: c# .net wpf entity-framework


    【解决方案1】:

    假设底层数据库是 MSSQL,这将比涉及删除单个实体对象的任何操作要好得多,很多

    foreach (var tableName in listOfTableNames)
    {
        context.ExecuteStoreCommand("TRUNCATE TABLE [" + tableName + "]");
    }
    

    当然,如果您的表有外键关系,您需要按正确的顺序设置表名列表,以便在清除任何主键表之前先清除外键表取决于。

    【讨论】:

    • 不过,请务必处置和更新您的数据上下文!
    • DELETE 与 TRUNCATE 相比的一个优势:对于 DELETE,角色 db_datawriter 就足够了,对于 TRUNCATE 则不然!这就是为什么我更喜欢 DELETE,只要性能足够。
    • 在 EF5 中需要这样: context.Database.ExecuteSqlCommand("TRUNCATE TABLE [" + tableName + "]");来自hare
    • TRUNCATE 不是事务性的!这就是为什么它很快。如果出现问题,您将遇到麻烦。
    • 并且触发器不会在所有数据库系统中触发
    【解决方案2】:

    仅供懒人参考,代码是我自己在寻找答案时想出来的:

    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)
            {
                context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableName));
            }
    
            context.SaveChanges();
        }
    }
    

    简短说明: 由于缺少权限,我不会截断表,如果这对您来说不是问题,请随意这样做。 __MigrationHistory 表被 where 语句忽略。

    更新:经过一些研究,我想出了更好的解决方案(不是很好,但只删除了必需的列):

    public static void ClearDatabase(DbContext context)
    {
        var objectContext = ((IObjectContextAdapter)context).ObjectContext;
        var entities = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace).BaseEntitySets;
        var method = objectContext.GetType().GetMethods().First(x => x.Name == "CreateObjectSet");
        var objectSets = entities.Select(x => method.MakeGenericMethod(Type.GetType(x.ElementType.FullName))).Select(x => x.Invoke(objectContext, null));
        var tableNames = objectSets.Select(objectSet => (objectSet.GetType().GetProperty("EntitySet").GetValue(objectSet, null) as EntitySet).Name).ToList();
    
        foreach (var tableName in tableNames)
        {
            context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableName));
        }
    
        context.SaveChanges();
    }
    

    【讨论】:

    • 再修改:使用反射查找表名。
    • 当 PONO 数据类型在其他程序集中声明时(Type.GetType 行失败),此解决方案将不起作用。否则,很好的解决方案!
    【解决方案3】:

    对于 EF 6:

    DbSet<Entity>.RemoveRange(DbSet<Entity>);
    

    【讨论】:

    • 这仍然是 EF Core 的最新版本。
    • 嗨,你能给我一个可执行的例子吗?我找不到像您在 EF 6.4 中建议的那样获取查询的方法。假设我用“dbContext”名称实例化了一个 DbContext,并且我有一个名为 DbSetExample 的 DbSet,现在如果我编写 dbContext.DbSetExample.RemoveRange(DbSetExample);我得到“名称 'DbSetExample' 在当前上下文中不存在”。我究竟做错了什么?我忘记了一些使用吗?谢谢!
    • @cesAR 作为暗中的野刺,DbSet 与您的DbContext 没有关联。尝试使用谷歌搜索,因为您的错误与此答案无关
    • @jolySoft 我做的第一件事是谷歌它(我觉得你的评论有点贬义)。一个好的和实用的例子是一个好的答案的一部分,我想我并没有什么不合时宜的要求。您始终可以选择:接受评论以使其更好,或取笑。
    • @cesAR 这并不是贬义,这个问答是关于从DbSet 中删除所有内容。如果您对“当前上下文中不存在名称 'DbSetExample'”有疑问,并且在 Internet 上找不到答案,或者您可以随时提出问题。你永远不知道它可能会帮助其他人:-)
    【解决方案4】:

    使用类似这样的代码遍历表:

    context.GetType().GetProperties()
    .Where(propertyInfo => propertyInfo.PropertyType == typeof(Table<>))
    .Select(propertyInfo => propertyInfo.GetValue(context, null) as ITable).ToList()
    .Foreach(table =>
    {
        //code that deletes the actual tables records.
    }
    );
    

    【讨论】:

      【解决方案5】:

      我想尝试改进@Wojciech Markowski 的出色回答。

      如果你像我一样懒惰并且不想检查外键约束, 你可以使用这个方法:

              private void ClearDatabase(TContext context)
          {
                  // disable all foreign keys
                  //context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable @command1 = 'ALTER TABLE ? NOCHECK CONSTRAINT all'");
      
                  List<string> tableNames = context.Database.SqlQuery<string>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME NOT LIKE '%Migration%'").ToList();
      
                  for (int i = 0; tableNames.Count>0; i++)
                  {
                      try
                      {
                          context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableNames.ElementAt(i % tableNames.Count)));
                          tableNames.RemoveAt(i % tableNames.Count);
                          i = 0;
                      }
                      catch { } // ignore errors as these are expected due to linked foreign key data             
                  }
      
      
                  context.SaveChanges();
          }
      

      ClearDatabase 方法遍历表列表并清理它们。如果找到 FK 约束,则捕获异常并转到下一个表。 最后所有表都将被删除。

      此外,如果您不介意放松所有 FK 约束,您可以通过以下行禁用所有这些约束:

      context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable @command1 = 'ALTER TABLE ? NOCHECK CONSTRAINT all'");
      

      还有一点: 如果要删除所有表而不只是清除它们,请替换以下行:

      context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableNames.ElementAt(i % tableNames.Count)));
      

      与:

      context.Database.ExecuteSqlCommand(string.Format("DROP TABLE {0}", tableNames.ElementAt(i % tableNames.Count)));
      

      我亲自在 Entity Framework 6 上通过代码优先迁移检查了这个答案。

      编辑:更好的版本:

              private void ClearDatabase(MrSaleDbContext context)
          {
              //Optional: disable all foreign keys (db-schema will be loosed).
              //context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable @command1 = 'ALTER TABLE ? NOCHECK CONSTRAINT all'");
      
              List<string> tableNames = context.Database.SqlQuery<string>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME NOT LIKE '%Migration%' AND TABLE_NAME NOT LIKE 'AspNet%'").ToList();
      
              for (int i = 0; tableNames.Count > 0; i++)
              {
                  try
                  {
                      //To delete all tables and not just clean them from data, replace "DELETE FROM {0}" in "DROP TABLE {0}":
                      context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableNames.ElementAt(i % tableNames.Count)));
                      tableNames.RemoveAt(i % tableNames.Count);
                      i = -1; //flag: a table was removed. in the next iteration i++ will be the 0 index.
                  }
                  catch (System.Data.SqlClient.SqlException e)   // ignore errors as these are expected due to linked foreign key data    
                  {                    
                      if ((i % tableNames.Count) == (tableNames.Count - 1))
                      {
                          //end of tables-list without any success to delete any table, then exit with exception:
                          throw new System.Data.DataException("Unable to clear all relevant tables in database (foriegn key constraint ?). See inner-exception for more details.", e);
                      }
      
                  }
      
              }
      

      catch 块中的 if 语句检查我是否在不删除任何表的情况下到达表列表的最后一个索引。在这种情况下,不要进入无限循环,而是抛出异常并退出 for。

      【讨论】:

      • 您也可以使用 LINQ 进行迭代:tableNames.ForEach(t => context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", t)));
      【解决方案6】:

      (在 .NetCore 中)您可以在表格上使用 RemoveRange 并将表格本身作为参数。

      Tablename.RemoveRange(Tablename);
      

      【讨论】:

        【解决方案7】:

        truncate 无法在外键中删除。

        然后我为 DbContext 做了扩展方法。

        用法很简单。

        db.Truncates(); // 删除所有表。

        db.Truncates("Test1", "Test2"); // 只删除“Test1,Test2”表

        public static class DbContextExtension
        {
            public static int Truncates(this DbContext db, params string[] tables)
            {
                List<string> target = new List<string>();
                int result = 0;
        
                if (tables == null || tables.Length == 0)
                {
                    target = db.GetTableList();
                }
                else
                {
                    target.AddRange(tables);
                }
        
                using (TransactionScope scope = new TransactionScope())
                {
                    foreach (var table in target)
                    {
                        result += db.Database.ExecuteSqlCommand(string.Format("DELETE FROM  [{0}]", table));
                        db.Database.ExecuteSqlCommand(string.Format("DBCC CHECKIDENT ([{0}], RESEED, 0)", table));
                    }
                    
                    scope.Complete();
                }
        
                return result;
            }
        
            public static List<string> GetTableList(this DbContext db)
            {
                var type = db.GetType();
        
                return db.GetType().GetProperties()
                    .Where(x => x.PropertyType.Name == "DbSet`1")
                    .Select(x => x.Name).ToList();
            }
        }
        

        【讨论】:

        • 非常好!我无法让 TRUNCATE 使用外键,即使先保存父级并处理上下文。
        【解决方案8】:

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

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

        【讨论】:

          【解决方案9】:

          我想贡献我的一份力量。

          我想出了这个查询,它完全符合作者的要求。这是 .NET 5。

          var query = "sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL; SET QUOTED_IDENTIFIER ON; DELETE FROM ?; ALTER TABLE ? CHECK CONSTRAINT ALL;'"
          context.Database.ExecuteSqlRaw(query);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-02-19
            • 2012-11-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多