我如何才能快速删除使用实体框架表中的所有行?

我目前使用:

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

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

还有其他选择吗?


当前回答

EF Core 7.0通过添加批量更新和删除语义一次性解决了这个问题:

await dataDB.Table.ExecuteDeleteAsync();

注意,此语法立即执行底层(SQL)命令从表中删除数据。它不需要跟踪实体、标记实体以便删除,也不需要等待UpdateDatabase对数据库执行事务。

还要注意,默认情况下,单个事务中不会包含多个ExecuteDelete和ExecuteUpdate命令。但是,可以使用DbContext事务api在事务中包装这些命令。

其他回答

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

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

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

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

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

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

假设dataDb是一个DbContext(而不是ObjectContext),你可以包装它并像这样使用方法:

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

我的解决方案,混合了我的想法,一些答案(Ron来自这个帖子,但也有这个和这个反思),并试图涵盖一些不同的条件。

它基于EF6,但它应该工作得很好,只是修复了一些扩展,如GetTableName<TEntity>。

我的解决方案:

使用扩展,所以你只需要DbSet启动 有一个行数阈值,以决定RemoveRange或SQL执行,以避免性能问题 SQL版本基于DELETE而不是TRUNCATE,以避免外键问题(当然,它必须符合您的要求) 有一个参数以内联保存更改

private const int RangeLimit = 100;

private static void ClearTable<TEntity>(this DbSet<TEntity> dataSet, bool saveChanges = true) where TEntity : class
{
    DbContext context = null;

    if (dataSet.Count() > RangeLimit)
    {
        context = dataSet.GetContext();
        context.Database.ExecuteSqlCommand($"DELETE FROM [{context.GetTableName<TEntity>()}]");
    }
    else
    {
        dataSet.RemoveRange(dataSet);
    }

    if (!saveChanges)
    {
        return;
    }

    if (context == null)
    {
        context = dataSet.GetContext();
    }
    context.SaveChanges();
}

private static DbContext GetContext<TEntity>(this DbSet<TEntity> dbSet)
    where TEntity : class
{
    var internalSet = dbSet
        .GetType()
        .GetField("_internalSet", BindingFlags.NonPublic | BindingFlags.Instance)
        ?.GetValue(dbSet);
    var internalContext = internalSet?.GetType().BaseType
        ?.GetField("_internalContext", BindingFlags.NonPublic | BindingFlags.Instance)
        ?.GetValue(internalSet);
    return (DbContext)internalContext?.GetType()
        .GetProperty("Owner", BindingFlags.Instance | BindingFlags.Public)
        ?.GetValue(internalContext, null);
}

public static string GetTableName<TEntity>(this DbContext context) where TEntity : class
{
    return (context as IObjectContextAdapter).ObjectContext.CreateObjectSet<TEntity>().EntitySet.Name;
}

对于一个名为Entries的数据库表,你所要做的就是:

databaseContext.Entries.ClearTable();

如果你想保存更改,或者不想保存更改:

databaseContext.Entries.ClearTable(false);

它基于反射,简化代码。当然,它有一些性能上的权衡,但是对于每个表反射一次,因此在这些条件下应该是完全可以接受的。

没有Foreach你也能做到

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

这将删除所有行

If

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