Delete all entities in Entity Framework

后端 未结 8 1666
天命终不由人
天命终不由人 2020-12-13 17:10

I want to delete content of all tables (all entities) using Entity Framework 4+. How can this be done?

相关标签:
8条回答
  • 2020-12-13 17:32

    This will perform much, much better than anything involving deleting individual entity objects, assuming the underlying database is MSSQL.

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

    Of course, if your tables have foreign-key relationships, you'll need to set up your list of table names in the proper order so that you clear foreign-key tables before you clear any primary-key tables that they might depend upon.

    0 讨论(0)
  • 2020-12-13 17:34

    truncate could not delete within foreign key.

    then I made extension method for DbContext.

    usage is simple.

    db.Truncates(); // all table deletes.

    db.Truncates("Test1", "Test2"); // only "Test1, Test2" table delete

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-13 17:35

    Just for lazy ones, code I came up myself when looking for the answer:

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

    Short explanation: I do not truncate tables due to lack of permissions, if it’s not a problem for you, feel free to do so. The table __MigrationHistory is ignored by the where statement.

    UPDATE: After some research I came up with better solution (not as nice but deletes only required columns):

    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();
    }
    
    0 讨论(0)
  • 2020-12-13 17:38

    I would like to try to improve the great answer of @Wojciech Markowski.

    If you are lazy like me and don't want to check for foreign keys constraints, you can use this method:

            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 method goes over the list of tables and clean them. if FK constraint is found then catch the exception and move on to the next table. at the end all tables will be deleted.

    Moreover, if you don't mind to loose all the FK constraints, you can disable all of them by the line:

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

    One thing more: If you want to delete all tables and not just clear them, then replace the line:

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

    with:

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

    I personally checked this answer on Entity Framework 6 with code-first migration.

    EDIT: better version:

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

    the if statement in catch block checks whether i reached the last index of the tables-list without deleting any table. In that case, instead of going in an infinite loop, throw exception and exit the for.

    0 讨论(0)
  • 2020-12-13 17:43

    For EF 6:

    DbSet<Entity>.RemoveRange(DbSet<Entity>);
    
    0 讨论(0)
  • 2020-12-13 17:44

    Iterate through the tables with a code something like this:

    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.
    }
    );
    
    0 讨论(0)
提交回复
热议问题