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

时间:2013-03-05 09:39:53

标签: c# sql linq entity-framework

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

我目前正在使用:

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

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

还有其他选择吗?

23 个答案:

答案 0 :(得分:260)

对于那些谷歌搜索并最终像我一样的人,这就是你目前在EF5和EF6中的表现:

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

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

答案 1 :(得分:183)

警告: 以下内容仅适用于小型表格(想想< 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();

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


为什么会这么慢?

  1. EF将获得所有行(VotingContext.Votes)
  2. 然后将使用他们的ID(不确定具体如何,无关紧要)来删除它们。
  3. 因此,如果您正在处理大量数据,那么您将终止SQL服务器进程(它将消耗所有内存)和IIS进程同样的事情,因为EF将以与SQL服务器相同的方式缓存所有数据。如果您的表包含大量数据,请不要使用此表。

答案 2 :(得分:82)

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

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

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

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

答案 3 :(得分:37)

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

答案 4 :(得分:30)

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

答案 5 :(得分:12)

这可以避免使用任何sql

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

答案 6 :(得分:10)

您可以在没有Foreach的情况下完成

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

这将删除所有行

答案 7 :(得分:8)

当我不得不处理特定案例时,我遇到了这个问题:完全更新“leaf”表中的内容(没有指向它的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失败)。

答案 8 :(得分: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。

答案 9 :(得分:4)

如果是

      using(var db = new MyDbContext())
            {
               await db.Database.ExecuteSqlCommandAsync(@"TRUNCATE TABLE MyTable"););
            }

原因

不能截断表格&#39; MyTable&#39;因为它是由FOREIGN KEY约束引用的。

我用这个:

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }

答案 10 :(得分:3)

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

答案 11 :(得分:3)

在EFCore(我正在使用的版本为3.1)中,您可以使用以下命令删除所有行-

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

答案 12 :(得分:1)

这在EF 5中正常运作:

YourEntityModel myEntities = new YourEntityModel();

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

答案 13 :(得分:1)

dataDb.Table.RemoveRange(dataDb.Table);
dataDb.SaveChanges();

答案 14 :(得分: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;
}

答案 15 :(得分:0)

在我的代码中,我实际上并没有很好地访问Database对象,因此您可以在DbSet上进行此操作,也可以使用任何类型的sql。最终会像这样结束:

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

答案 16 :(得分:0)

以下内容适用于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异常。 / p>

注意:var context = new YourContext()

答案 17 :(得分:0)

如果使用MVC,则可以:

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

答案 18 :(得分:0)

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

答案 19 :(得分:0)

var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();

答案 20 :(得分:0)

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

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

答案 21 :(得分:0)

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

>

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

_dbContext.TruncateTable<TheTableName>();

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

这是扩展类:

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 替代方案快得多。

答案 22 :(得分:-1)

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

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

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

免责声明:我不隶属于 nuget 包。他们确实提供了一个付费版本,可以做更多的事情。