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

我目前使用:

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

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

还有其他选择吗?


当前回答

确保当你试图删除父级时,所有的子级将在删除时级联。或者子节点的外键为空。

其他回答

using (var context = new DataDb())
{
     var ctx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext;
     ctx.ExecuteStoreCommand("DELETE FROM [TableName] WHERE Name= {0}", Name);
}

or

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
context.TableName.RemoveRange(context.TableName);
context.SaveChanges();

我在处理一个特殊情况时遇到了这个问题:完全更新“叶”表中的内容(没有指向它的fk)。这涉及到删除所有行并放入新行信息,这应该以事务方式完成(如果由于某种原因插入失败,我不想以空表结束)。

我已经尝试了公共静态void Clear<T>(此DbSet<T> DbSet)方法,但没有插入新行。另一个缺点是整个过程很慢,因为一行一行地删除。

因此,我已经切换到TRUNCATE方法,因为它更快,而且它也是可回滚的。它还重置标识。

使用存储库模式的示例:

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失败)。

工作EF核心3

public static class EntityExtensions
{
    public static async Task ClearAsync<T>(this DbSet<T> dbSet) where T : class
    {
        var command = dbSet.CreateDbCommand();
        command.CommandText = $"TRUNCATE TABLE {dbSet.EntityType.GetSchema()}.{dbSet.EntityType.GetTableName()}";
        await command.ExecuteNonQueryAsync();
    }
}

但是请注意dbSet。CreateDbCommand是一个扩展

我的解决方案,混合了我的想法,一些答案(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);

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