我正在使用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做这种事情是正常的吗?
答案 0 :(得分:83)
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
相比,使用AddRange
和Remove()
项(特别是,如您所说,不常见)将为您节省大量时间。
答案 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;