关于c#:Entity Framework。 删除表中的所有行

Entity Framework. Delete all rows in table

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

我目前正在使用:

1
2
3
4
5
6
7
var rows = from o in dataDb.Table
           select o;
foreach (var row in rows)
{
    dataDb.Table.Remove(row);
}
dataDb.SaveChanges();

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

还有其他选择吗?


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

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

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


警告:以下内容仅适用于小型表(想想<1000行)

这是一个使用实体框架(而不是SQL)来删除行的解决方案,因此它不是特定于SQL Engine(R / DBM)的。

这假设您正在进行测试或类似情况。

  • 数据量很小或
  • 表现无关紧要

只需致电:

1
VotingContext.Votes.RemoveRange(VotingContext.Votes);

假设这个背景:

1
2
3
4
5
6
7
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;}
}

对于更整洁的代码,您可以声明以下扩展方法:

1
2
3
4
5
6
7
public static class EntityExtensions
{
    public static void Clear< T >(this DbSet< T > dbSet) where T : class
    {
        dbSet.RemoveRange(dbSet);
    }
}

然后上面变成:

1
2
3
4
5
VotingContext.Votes.Clear();
VotingContext.Voters.Clear();
VotingContext.Candidacy.Clear();
VotingContext.Polls.Clear();
await VotingTestContext.SaveChangesAsync();

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

为什么它会慢?

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


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

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

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

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


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


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

    要么

    1
    2
    3
    4
    using (var context = new DataDb())
    {
         context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
    }


    这可以避免使用任何sql

    1
    2
    3
    4
    5
    6
    using (var context = new MyDbContext())
    {
        var itemsToDelete = context.Set<MyTable>();
        context.MyTables.RemoveRange(itemsToDelete);
        context.SaveChanges();
    }


    没有Foreach,你可以做到这一点

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

    这将删除所有行


    当我不得不处理一个特定的案例时,我遇到了这个问题:完全更新"叶子"表中的内容(没有指向它的FK)。这涉及删除所有行并放置新行信息,它应该以事务方式完成(我不希望最终得到一个空表,如果插入因任何原因失败)。

    我尝试了public static void Clear< T >(this DbSet< T > dbSet)方法,但没有插入新行。另一个缺点是整个过程很慢,因为行被逐个删除。

    所以,我已经切换到TRUNCATE方法,因为它更快,并且它也是ROLLBACKable。它也重置了身份。

    使用存储库模式的示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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失败)不能使用此解决方案。


    如果您希望清除整个数据库。

    由于外键约束,表格被截断的顺序很重要。这是一种强制执行此序列的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
        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();
            }
        }

    用法:

    1
    ClearDatabase<ApplicationDbContext>();

    记得在此之后重新实现你的DbContext。


    如果

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

    原因

    无法截断表'MyTable',因为它正被FOREIGN KEY约束引用。

    我用这个:

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


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


    这在EF 5中正常工作:

    1
    2
    3
    4
    YourEntityModel myEntities = new YourEntityModel();

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

    删除所有记录。不要像"truncate"那样重置主索引。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /// <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;
    }