63

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

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Cartesius00
  • 23,584
  • 43
  • 124
  • 195

9 Answers9

54

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.

Joel Mueller
  • 28,324
  • 9
  • 63
  • 88
  • 4
    Be sure to dispose and renew your data context, though! – Vinzz Jun 12 '12 at 11:57
  • 3
    One advantage of DELETE in comparison to TRUNCATE: for DELETE the role db_datawriter is sufficient, for TRUNCATE it's not! This is why I prefer DELETE as long as performance is sufficient. – Tillito Aug 23 '12 at 13:24
  • 4
    in EF5 that need to be like this : context.Database.ExecuteSqlCommand("TRUNCATE TABLE [" + tableName + "]"); that came from [hare](http://stackoverflow.com/questions/13857242/where-is-executestorecommand-in-entity-framework-5) – oCcSking May 20 '13 at 15:06
  • 1
    `TRUNCATE` is not transactional! That's why it is fast. If something goes wrong, you will be in trouble. – Tikkes Sep 11 '13 at 12:07
  • and the trigger do not fire in all db systems – Mario The Spoon Jun 25 '15 at 17:39
17

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();
}
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Wojciech Markowski
  • 1,074
  • 1
  • 11
  • 15
9

For EF 6:

DbSet<Entity>.RemoveRange(DbSet<Entity>);
jolySoft
  • 2,948
  • 2
  • 30
  • 34
  • 5
    This is still current for EF Core. – Grimley Aug 09 '16 at 11:43
  • Hi, can you give me an executable example? I can't find the way to get a query like you are suggesting with EF 6.4. Let's suppose I instantiate a DbContext with 'dbContext' name, and I have a DbSet called DbSetExample, now if I write dbContext.DbSetExample.RemoveRange(DbSetExample); I get "The name 'DbSetExample' does not exist in the current context". What am I doing wrong? Am I forgetting some using? Thank! – cesAR Jan 17 '20 at 19:37
  • @cesAR as wild stab in the dark, the `DbSet` is not associated with your `DbContext`. Try googling it as your error doesn't really relate to this answer – jolySoft Jan 28 '20 at 10:47
  • @jolySoft the first thing i have made was google it (I felt your comment a little bit derogatory). A good and functional example is part of a good answer, I think I'm not asking for nothing out of place. You always can choose: take the comment to make it better, or make fun. – cesAR Jan 29 '20 at 12:51
  • @cesAR it is not meant to be derogatory, this Q&A is about removing everything from a `DbSet`. If you have a question about 'The name 'DbSetExample' does not exist in the current context' and you can't find an answer on the internet or SO you can always ask a question. You never know it might help some else :-) – jolySoft Jan 31 '20 at 11:09
3

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.

Dudi
  • 3,069
  • 1
  • 27
  • 23
  • You could also use LINQ to iterate: tableNames.ForEach(t => context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", t))); – Mill Nov 21 '19 at 12:51
  • `int records = context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableName));` (---NEW LINE---) `if (records > 0) context.Database.ExecuteSqlCommand(string.Format("DBCC CHECKIDENT ('{0}', RESEED, 0)", tableName));` – Valamas May 22 '23 at 07:02
  • otherwise you will get a seeding 'bug'. Clearing the DB and always running reseed on a fresh database cause the ID to become 0 and not 1 for the first record. – Valamas May 22 '23 at 07:05
3

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.
}
);
H H
  • 263,252
  • 30
  • 330
  • 514
Peter
  • 398
  • 3
  • 15
2

(In .NetCore) You can use RemoveRange on the table with the table itself as Parameter.

Tablename.RemoveRange(Tablename);
1

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();
    }
}
Community
  • 1
  • 1
Kim Ki Won
  • 1,795
  • 1
  • 22
  • 22
1

This works for me... EF v3.1.5

context.ModelName.RemoveRange(context.ModelName.ToList());
context.SaveChanges();
Nismi Mohamed
  • 670
  • 1
  • 7
  • 7
0

I want to contribute my share.

I came up with this query, which does exactly what the author requested. This is .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);
Arthur Edgarov
  • 473
  • 5
  • 16