如何使用 EF6 删除 1,000 行?

Posted

技术标签:

【中文标题】如何使用 EF6 删除 1,000 行?【英文标题】:How can I delete 1,000 rows with EF6? 【发布时间】:2014-03-01 08:47:31 【问题描述】:

我正在使用实体框架 6。

我有一个名为 Tests 的包含测试信息的表。我正在删除 通过首先获取测试列表,从该表中获取行,执行 删除每个然后提交。

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

我有另一个名为 Questions 的包含问题信息的表格。 我也想做同样的事情,但其中有超过 1000 行 桌子。如果我将它们全部列出,然后进行 1,000 次删除,这将是 效率不是很高。

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

【问题讨论】:

也可以在不检索对象的情况下删除它 - 有一些警告 - blogs.msdn.com/b/alexj/archive/2009/03/27/… 【参考方案1】:

据我所知,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<>

编辑

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

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

context.Casinos.AddRange(new[] Casino1, Casino2 );这些方法 执行速度比一次添加或删除单个对象快得多 因为,默认情况下,Entity Framework 在每个 Add 中调用 DetectChanges 和删除方法。使用 Range 方法,您可以处理多个 对象而 DetectChanges 只被调用一次,提高了性能 戏剧性地。我已经用 5、50、500、5,000 甚至 50,000 个对象,至少在我的场景中,没有限制 数组的大小——而且速度惊人!请记住,这 改进仅与使上下文对 对象,并且与 SaveChanges 无关。仍然调用 SaveChanges 一次只执行一个数据库命令。所以虽然你可以快速 将 50,000 个对象添加到上下文中,您仍将获得 50,000 个插入 调用 SaveChanges 时单独执行的命令——可能不会 你想在真实系统中做的事情。

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

如果您真的只想发出单个数据库命令,则可以选择使用原始 SQL 语句的存储过程,因为 EntityFramework 不支持批量事务。但是,与在 foreach 循环中调用 Remove() 相比,使用 RemoveRangeAddRange 项目(特别是如果,如您所说,不经常使用)将为您节省大量时间。

【讨论】:

谢谢。我现在会调查一下。 这似乎是一个不错的选择,但我不清楚它是如何工作的。我感觉它仍然会对数据库发出 1000 次删除。 @SamanthaJ - 我添加了一些关于这实际上是如何工作的更多细节。但是,您是对的,因为该方法经过优化,您将看到 foreach 方法的性能提升。如果您只需要一个命令,您将被降级为存储过程或创建自己的 SQL 语法并发送到服务器。【参考方案2】:

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

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

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

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

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

【讨论】:

仅供参考,因为我经常收到有关此 Q&A 的消息...我只是去看看那个图书馆,我认为应该谨慎行事。它自 2015 年 7 月以来一直没有更新,目前有 117 个未解决的问题。我不确定它是否仍在维护。 这让我很担心,也许他们现在直接为 EF 做贡献,因为那是开源的? 只是为了帮助其他可能想走 EF-Plus 路线的人;他们已经对其进行了重新改进,并在 5 天前对其进行了更新。似乎它又恢复了维护业务。 github.com/zzzprojects/EntityFramework-Plus【参考方案3】:

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

使用 .RemoveRange()

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

exec sp_executesql N'SELECT [Extent1].[Id] AS [Id], [Extent1].[IdOrder] AS [IdOrder], [Extent1].[名称] AS [名称], [Extent1].[Partita] AS [Partita], FROM [dbo].[MyTable] AS [Extent1] WHERE [Extent1].[IdOrder] = @p__linq__0',N'@p__linq__0 varchar(8000)',@p__linq__0='0cb41f32-7ccb-426a-a159-b85a4ff64c29'

然后它会向数据库触发 N 个删除命令

exec sp_executesql N'DELETE [dbo].[MyTable] WHERE ([Id] = @0)',N'@0 varchar(50)',@0='ffea29aa-8ba5-4ac9-871b-3f5979180006'

X 1000 次

这也发生在使用和 IQueriable

使用实体框架扩展库

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

exec sp_executesql N'DELETE [dbo].[MyTable] FROM [dbo].[MyTable] AS j0 内连接(选择 1 作为 [C1]、[Extent1].[Id] 作为 [Id] FROM [dbo].[MyTable] AS [Extent1] WHERE [Extent1].[IdOrder] = @p__linq__0) AS j1 开启 (j0.[Id] = j1.[Id])',N'@p__linq__0 nvarchar(36)',@p__linq__0=N'0cb41f32-7ccb-426a-a159-b85a4ff64c29'

【讨论】:

如上面的答案所述,您的测试确认 EF 不进行批量交易。我仍然使用 EF 扩展库分享我的担忧,因为它自 2015 年 7 月以来仍未更新,现在有大约 145 个未解决的报告问题。恕我直言,最好的方法仍然是调用存储过程或让 EF 直接执行您想要的 SQL 语句,例如 ctx.Database.ExecuteSqlCommand("DELETE FROM MyTable WHERE myColumn = someValue"); 来源:msdn.microsoft.com/en-us/library/jj592907%28v=vs.113%29.aspx 我同意你的看法。我很害怕由于某种原因这个扩展库会在 sql 中产生不正确的翻译,并会删除错误的记录。

以上是关于如何使用 EF6 删除 1,000 行?的主要内容,如果未能解决你的问题,请参考以下文章

使用 EF6 进行单元测试后重建数据库

如何从实体框架 4 升级到 EF6?

MySQL via EF6 的试用报告

使用 Pandas 时如何打印多达 3,000 行数据框 [重复]

如何使用具有大量行的 DataFrame 使线图可读

如何使用 Python 比较 2 个非常大的矩阵