如何在DbSet中移除所有元素?

96

如何在Entity Framework 4.3中最好地删除System.Data.Entity.DbSet中的所有元素?

7个回答

118
dbContext.Database.ExecuteSqlCommand("delete from MyTable");

不开玩笑。

问题在于EF不支持任何批处理命令,而使用没有直接DML的方式删除集合中所有实体的唯一方法是:

foreach (var entity in dbContext.MyEntities)
    dbContext.MyEntities.Remove(entity);
dbContext.SaveChanges();

或许可以稍微便宜一点,以避免加载完整的实体:

foreach (var id in dbContext.MyEntities.Select(e => e.Id))
{
    var entity = new MyEntity { Id = id };
    dbContext.MyEntities.Attach(entity);
    dbContext.MyEntities.Remove(entity);
}
dbContext.SaveChanges();

但是,在这两种情况下,您都必须加载所有实体或所有关键属性,并逐个从集合中删除实体。此外,当您调用 SaveChanges 时,EF 将向数据库发送n(=集合中实体的数量)个 DELETE 语句,这些语句也将在数据库中逐个执行(在单个事务中)。

因此,直接使用 SQL 明显更可取,因为您只需要一个单独的 DELETE 语句。


2
在使用 dbContext.Database.ExecuteSqlCommand 后,您需要调用 SaveChanges 吗? - PeterX
真正的问题是DbContext如何提供对其所有DbSet/实体的访问?除非使用反射,否则无法循环遍历它们。当您提到“我的实体”时,您是否考虑过这一点? - Veverke
2
RemoveRange 代替 Remove 怎么样?你会说,“这只是在 EF 6 中引入的,而 OP 问的是关于 EF 4.* 的”吗? - Nate Anderson

77

虽然这是一篇旧文章,但现在有一个RemoveRange方法:

    dbContext.MyEntities.RemoveRange(dbContext.MyEntities);
    dbContext.SaveChanges();

2
我使用这个答案是因为代码简单明了。我相信有更有效的方法来完成这个任务,但我现在正在使用一个将在夜间运行的Azure WebJob,所以这不是一个问题。 - Phil Ringsmuth
RemoveRange的问题在于,你仍然需要从数据库中查询所有实体(或至少是它们的ID),因为这个方法只是一个foreach的包装器。 - Szörényi Ádám
请添加文档链接! - jasie

18

以下是您可以在代码中使用的另一种方法。

public static class Extensions
{
    public static void DeleteAll<T>(this DbContext context)
        where T : class
    {
        foreach (var p in context.Set<T>())
        {
            context.Entry(p).State = EntityState.Deleted;
        }
    }
}

要实际调用方法并清除集合:

myDbContext.DeleteAll<MyPocoClassName>();

4
如果您想要删除所有元素而不编写任何SQL语句,并且只执行单个数据库调用Entity Framework Extended Library 提供了一个批量删除方法。
context.Users.Delete();

1
你还需要在之后调用 context.SaveChanges() 吗?如果不需要的话,我会认为扩展库中的 Delete() 方法是在上一次调用 context.SaveChanges() 之后发生在一个独立的事务中的,与其他数据库更改无关。 - J.D.

2
作为被接受的答案仅提到以下方法:
context.Database.ExecuteSqlCommand("delete from MyTable");

相反地,我已经编写了一种方法,可以避免加载所有实体,然后通过循环使用ExecuteSqlCommand,而不是直接加载所有实体。

假设使用工作单元,在其中上下文是DbContext:

using System.Data.Entity.Core.Objects;
using System.Text.RegularExpressions;

public void DeleteAll()
{
    ObjectContext objectContext = ( (IObjectContextAdapter)context ).ObjectContext;
    string sql = objectContext.CreateObjectSet<T>().ToTraceString();
    Regex regex = new Regex( "FROM (?<table>.*) AS" );
    Match match = regex.Match( sql );
    string tableName = match.Groups[ "table" ].Value;

    context.Database.ExecuteSqlCommand( string.Format( "delete from {0}", tableName ) );
}

第一个代码块检索在ExecuteSqlCommand方法中所需的表格名称。

使用方法:

using ( var context = new UnitOfWork() )
{
    context.MyRepository.DeleteAll();
}

不需要打电话

了解

context.SaveChanges()

1
如果你正在使用工作单元和通用仓储库,你可能会发现以下内容很有用。
public virtual void DeleteWhere(Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<TEntity> query = dbSet;
            if (filter != null)
            {
                query = query.Where(filter);
            }
            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            foreach (var entity in query)
            {
                context.Entry(entity).State = EntityState.Deleted;
            }
        }

使用方法:

uow.myRepositoryName.DeleteWhere(u => u.RoomId == roomId);
uow.Save();

-1
使用仓储模式,您可以将模型类型传递给仓储,从而使其能够处理任何模型类型。
    public async Task<int> RemoveAllAsync()
    {
        Context.Set<T>().RemoveRange(await Context.Set<T>().ToListAsync());
        return await Context.SaveChangesAsync();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接