使用RemoveRange()批量删除行

27

我尝试从一张表中删除多行记录。

在常规的SQL Server中,这很简单,就像这样:

DELETE FROM Table
WHERE
    Table.Column = 'SomeRandomValue'
    AND Table.Column2 = 'AnotherRandomValue'
在Entity Framework 6中,他们引入了RemoveRange()方法。
然而,当我使用它时,它并没有使用我提供的where子句来删除行,而是通过其主键一个个查询匹配的所有行并将其删除。
这是Entity Framework的当前限制吗? 还是我错误地使用了RemoveRange()方法?
以下是我如何使用RemoveRange()的方式:
db.Tables.RemoveRange(
    db.Tables
        .Where(_ => _.Column == 'SomeRandomValue'
            && _.Column2 == 'AnotherRandomValue')
);

你的代码库叫做Tables吗? - Jeremy Holovacs
3
EntityFramework.Extended 包含 批量删除 功能。 - crthompson
1
请查看以下链接:https://dev59.com/HnE85IYBdhLWcg3w_Itr - Cristi Pufu
1
@CristiPufu和@paqogomez:是的,我正在查看EntityFramework.Extended。不幸的是,微软构建了RemoveRange()函数,但仍然需要循环。 - Peter Han
1
我相信RemoveRange是社区贡献的,比自己循环一堆实体要快一点,但它仍然是逐个删除它们。在EF中没有内置批量删除的支持(除了运行自己的SQL)。 - ESG
显示剩余10条评论
7个回答

7

我认为我们已经达到了EF的限制。 有时你只需要使用ExecuteSqlCommand来保持高性能。


5
你需要的是一个批量删除库,可以在不加载实体的情况下从LINQ查询中删除数据库中的多条记录。
有多个支持此功能的库可用。
您可以在这里找到列表:Entity Framework批量删除库免责声明: 我是项目Entity Framework Plus的所有者。
// using Z.EntityFramework.Plus; // Don't forget to include this.

// DELETE directly in SQL (without loading entities)
db.Tables.Where(_ => _.Column == 'SomeRandomValue'
                     && _.Column2 == 'AnotherRandomValue')
         .Delete();

// DELETE using a BatchSize      
db.Tables.Where(_ => _.Column == 'SomeRandomValue'
                     && _.Column2 == 'AnotherRandomValue')
         .Delete(x => x.BatchSize = 1000);

维基百科:EF+ 批量删除


1

它有点破碎,请尝试

db.Tables.RemoveRange(
    db.Tables
        .Where(_ => _.Column == 'SomeRandomValue'
            && _.Column2 == 'AnotherRandomeValue').AsEnumerable().ToList()
);
db.SaveChanges();

1
抱歉,使用这段代码后我仍然得到相同的结果。Entity Framework 执行内部查询,然后循环遍历每一行并将其删除。 - Peter Han

0
var db1 =  db.Tables
        .Where(_ => _.Column == 'SomeRandomValue'
            && _.Column2 == 'AnotherRandomeValue').AsEnumerable().ToList();
db.Tables.RemoveRange(db1);
db.SaveChanges();

使用变量存储可移动列表并将其传递给RemoveRange()。

通常我会这样做,而且这个方法很有效。希望在你的情况下也适用。


2
抱歉,使用这段代码后仍然得到相同的结果。Entity Framework 执行内部查询,然后循环遍历每一行并将其删除。 - Peter Han
如果您的表具有外键,请在模型中使用 [Foreign] 属性指定,并使用虚拟属性。 - AKASH

0

退一步思考。您真的想要从数据库中下载记录以便删除它们吗?仅仅因为您可以这样做并不意味着这是一个好主意。

也许您可以考虑使用存储过程从数据库中删除项目?EF 也允许这样做...


1
我同意在这种情况下存储过程是更好的解决方案。然而,我也认为这是EF的一个缺点。删除符合特定条件的一堆行是基本的CRUD操作。使用存储过程来做这件事的事实背离了使用ORM的初衷。 - Peter Han
有一个被称为 "ORM 阻抗不匹配" 的东西,它的要点是数据库设计用于:
  • 快速执行 CRUD 操作
  • 在磁盘上高效占用空间
  • 保证引用完整性
而不仅仅是在重启后保持数据可用或实现跨系统边界共享数据。
- Walter Verhoeven

0

我正在处理这个问题,同意Adi的意见 - 只需使用SQL。

我正在清理日志表中的旧行,EF RemoveRange花了3分钟做同样的事情,而这个只需要3秒钟:

DELETE FROM LogEntries WHERE DATEDIFF(day, GETDATE(), Date) < -6

日期是包含日期的列的名称。为了使其正确,当然要使用参数,就像这样:

  context.Database.ExecuteSqlCommand
      ("DELETE FROM  LogEntries WHERE DATEDIFF(day, GETDATE(), Date) < @DaysOld", new System.Data.SqlClient.SqlParameter(
                        "DaysOld", - Settings.DaysToKeepDBLogEntries));

请注意我的情况涉及到很多行数据。当我开始项目并且数据不是很多时,RemoveRange 的效果还不错。

0
我也遇到了这个问题,我的解决方法是使用一个名为 entityframework.extended 的库,它由LoreSoft提供(你可以从nuget包管理器中获取):
1.首先查询您的列表。 2.然后使用 .extended 库中的 .Delete() 函数。
var removeList = db.table.Where(_ => _.Column == 'SomeRandomValue'&& _.Column2 == 'AnotherRandomValue')        
removeList .Delete();

注意:根据Entity Framework EF extended,截至撰写本文时,此entityframework.extended已被弃用。因此,可能需要考虑使用entityframework plus库。虽然我尚未在plus扩展上进行测试,但我可以确认它可以使用.extended库正常工作。

你可能需要解释一下.Delete()是来自外部库而不是实体框架的一部分。 - Ashley Medway
1
@csamleong,这个问题中有多个评论和答案指向Entity Framework Extended。我的问题的目标是看看是否有一种原始的EF方法来解决这个问题。根据我得到的答案,似乎没有。虽然我已经放弃了这个问题并继续前进,但很高兴看到这个问题仍然困扰着很多人。希望EF能尽快找到解决方案或将EF Extended纳入其自身的产品中。 - Peter Han

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