如何用EF6删除1,000行?

Sam*_*tar 39 entity-framework entity-framework-6

我正在使用Entity Framework 6.

我有一个表格,里面有一个名为Tests的测试信息.我通过首先获取测试列表,为每个测试执行删除然后提交来删除此表中的行.

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

我有另一个表格,其中包含问题信息.我想做同样的事情,但这张表中有超过1000行.如果我将它们全部列出然后执行1,000次删除将不会非常有效.

这种问题的删除并不经常发生.有没有人建议如何做到这一点.我应该做1000次删除吗?使用EF做这种事情是正常的吗?

Tom*_*mmy 88

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

var db = new MyDbContext();
var itemsToDelete = db.MyTable.Where(x=>!x.active);
db.MyTable.RemoveRange(itemsToDelete);
db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

因此foreach,您可以使用这种新的扩展方法,而不必执行任何类型的操作.使用Unit Of Work上下文,您可以Delete使用IEnumerable(?*)而不是Test像当前方法那样的单个对象来重载您的方法.这个新的重载应该调用RemoveRange()DbContext上的函数.

?* - 这取决于什么GetTests()回报,但我认为IEnumerable<>涵盖了一个IList<>和一个IQueryable<>

编辑

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

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

context.Casinos.AddRange(new [] {casino1,casino2}); 这些方法的执行速度比一次添加或删除单个对象要快得多,因为默认情况下,Entity Framework会在每个Add和Remove方法中调用DetectChanges.使用Range方法,您可以处理多个对象,而DetectChanges只被调用一次,从而显着提高性能.我已经使用5个,50个,500个,5,000个甚至50,000个对象测试了这个,至少在我的场景中,对阵列的大小没有限制 - 而且它的速度非常快!请记住,此改进仅与获取上下文以对象操作相关,并且与SaveChanges无关.调用SaveChanges仍然一次只执行一个数据库命令.因此,虽然您可以快速将50,000个对象添加到上下文中,但在调用SaveChanges时仍然可以单独执行50,000个插入命令 - 可能不是您想要在实际系统中执行的操作.

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

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

  • 这似乎是一个不错的选择,但我不清楚它是如何工作的。我有一种感觉,它将仍然对数据库发出1000次删除。 (2认同)

Ane*_*lou 12

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

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

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

// Deleting
context.Users
  .Where(u => u.FirstName == "firstname")
  .Delete();
Run Code Online (Sandbox Code Playgroud)

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

  • 仅仅是因为我经常收到关于这个Q&A的消息...我只是去看看那个库,我认为应该谨慎使用.它自2015年7月以来尚未更新,目前有117个未决问题.我不确定它是否仍在维护中. (6认同)
  • 只是为了帮助其他可能想走EF-Plus路线的人;他们已经对其进行了重新升级,并于5天前进行了更新。似乎恢复了业务。https://github.com/zzzprojects/EntityFramework-Plus (3认同)

Mar*_*oli 5

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

使用 .RemoveRange()

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

exec sp_executesql N'SELECT [Extent1].[Id] AS [Id], [Extent1].[IdOrder] AS [IdOrder], [Extent1].[Name] AS [Name], [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 INNER JOIN (SELECT 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])',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");` 来源:https: //msdn.microsoft.com/en-us/library/jj592907%28v=vs.113%29.aspx (2认同)
  • 我同意你的看法。我很害怕由于某种原因这个扩展库会在 sql 中产生不正确的翻译,并会删除错误的记录。 (2认同)