如何用EF6删除1,000行?

时间:2014-02-05 04:10:25

标签: entity-framework entity-framework-6

我正在使用Entity Framework 6。

我有一个表格,其中包含名为“测试”的测试信息。我正在删除 这个表中的行首先得到一个测试列表 删除每个然后提交。

   var testList = _testService.GetTests(1, userId).ToList();
   testList.ForEach(_obj => _uow.Tests.Delete(_obj));
   _uow.Commit();

我有另一张表格,其中包含一些名为“问题”的问题 我想做同样的事情,但这里有超过1000行 表。如果我列出所有这些,那么将删除1,000次 不是很有效率。

这种问题的删除并不经常发生。是否 任何人都有关于如何做到这一点的建议。我应该这样做 1,000删除。使用EF做这种事情是正常的吗?

3 个答案:

答案 0 :(得分:83)

据我所知,EF 6在你的DbContext上引入了.RemoveRange()选项。简而言之,您可以执行以下操作:

var db = new MyDbContext();
var itemsToDelete = db.MyTable.Where(x=>!x.active);
db.MyTable.RemoveRange(itemsToDelete);
db.SaveChanges();

因此,您无需使用任何类型的foreach,而是可以使用此新的扩展方法。使用您的工作单元上下文,您可能会使用Delete方法重载,该方法采用IEnumerable(?*)而不是单个Test对象,就像当前方法一样。这个新的重载应该调用DbContext上的RemoveRange()函数。

?* - 这取决于GetTests()返回的内容,但我认为IEnumerable<>涵盖了IList<>IQueryable<>

修改

几条评论。首先,我不会在发出.ToList()之前致电RemoveRange,因为您不想将这些项目实际提取到您的服务中。这应该有助于减少一些性能时间。其次,你是对的,你还会发出1000个删除语句。但是,性能提升来自于不在EF中为您从DbSet删除的每个项目调用ChangeTracker。来自MSDN magazine

  

AddRange和RemoveRange如前所述,AddRange和   RemoveRange是社区成员Zorrilla的贡献。每   method将参数作为单个实体类型的可枚举。   在共享DbTransactions部分的第一个代码示例中,我使用了   传入Casino实例数组时的AddRange:

     

context.Casinos.AddRange(new [] {casino1,casino2});这些方法   执行速度比一次添加或删除单个对象快得多   因为,默认情况下,Entity Framework会在每个Add中调用DetectChanges   和删除方法。使用Range方法,您可以处理多个   DetectChanges仅被调用一次,从而提高了性能   大幅提升。我用5,50,500,5,000甚至是5来测试过这个   50,000个对象,至少在我的场景中,没有限制   阵列的大小 - 它的速度非常快!请记住这一点   改善只与获取上下文的行为有关   对象,与SaveChanges无关。仍然调用SaveChanges   一次只执行一个数据库命令。所以尽管你可以很快   在上下文中添加50,000个对象,您仍然可以获得50,000个插入   调用SaveChanges时单独执行的命令 - 可能不是   你想在真实系统中做些什么。

     

另一方面,有很长时间的讨论   在不需要对象的情况下实现批量操作的支持   由EF(bit.ly/16tMHw4)跟踪,并启用批处理操作   在一次调用数据库中一起发送多个命令   (bit.ly/PegT17)。这两个功能都没有进入最初的EF6版本,   但两者都很重要,并且将来会发布。

如果您确实只想发出单个数据库命令,那么使用原始SQL语句的存储过程将成为可行的方法,因为EntityFramework不支持批量事务。但是,与在foreach循环中调用RemoveRange相比,使用AddRangeRemove()项(特别是,如您所说,不常见)将为您节省大量时间。

答案 1 :(得分:12)

内置实体框架 .RemoveRange()方法,仍在内存中获取条目,并发出 X 删除所有这些条目的循环。

如果您不想编写任何SQL for Deletion ,尤其是在选择要删除的实体是复杂的时

Entity Framework Plus Library 提供仅发出一个命令的批量删除更新方法。

// Deleting
context.Users
  .Where(u => u.FirstName == "firstname")
  .Delete();
  

实体框架的当前限制是,为了更新或删除实体,您必须首先将其检索到内存中。现在在大多数情况下,这很好。然而,有一些senerios性能会受到影响。此外,对于单个删除,必须先检索该对象,然后才能删除该对象,从而需要对数据库进行两次调用。批量更新和删除消除了在修改实体之前检索和加载实体的需要。

答案 2 :(得分:2)

我使用EF6和Sql Server Profiler进行了一些测试

使用.RemoveRange()

首先获取要从数据库中删除的所有记录

  

exec sp_executesql N&#39; SELECT       [Extent1]。[Id] AS [Id],       [Extent1]。[IdOrder] AS [IdOrder],       [Extent1]。[Name] AS [Name],       [Extent1]。[Partita] AS [Partita],       FROM [dbo]。[MyTable] AS [Extent1]       在哪里[Extent1]。[IdOrder] = @ p__linq__0&#39;,N&#39; @ p__linq__0   VARCHAR(8000)&#39;,@ p__linq__0 =&#39; 0cb41f32-7ccb-426A-A159-b85a4ff64c29&#39;

然后它触发N删除命令到数据库

  

exec sp_executesql N&#39; DELETE [dbo]。[MyTable] WHERE([Id] = @ 0)&#39;,N&#39; @ 0   VARCHAR(50)&#39;,@ 0 =&#39; ffea29aa-8ba5-4ac9-871b-3f5979180006&#39;

     

X 1000次

这也发生在使用和IQueriable

使用Entity Framework扩展库

它只向数据库发送一个命令

  

exec sp_executesql N&#39; DELETE [dbo]。[MyTable] FROM [dbo]。[MyTable] AS   j0 INNER JOIN(选择1 AS [C1],[Extent1]。[Id] AS [Id] FROM   [dbo]。[MyTable] AS [Extent1] WHERE [Extent1]。[IdOrder] = @ p__linq__0)   AS j1 ON(j0。[Id] = j1。[Id])&#39;,N&#39; @ p__linq__0   为nvarchar(36)&#39;,@ p__linq__0 = N&#39; 0cb41f32-7ccb-426A-A159-b85a4ff64c29&#39;