在 foreach 循环内部和外部调用 SaveChanges() 有什么区别吗?

19

如果在foreach循环内部对EF实体进行更改,调用EF SaveChanges()方法和在循环外部调用有没有性能上的优劣差别/技术差异?

4个回答

43

是的!

如果你在循环内调用它,EF将为每个实体(每个实体将在自己的单独事务中)将更改写回数据库。

反之,您将进行所有更改,EF将在循环后一次性将它们全部写回(对于所有实体一起使用一个单独的事务)。

作为一般经验法则(在没有实际查看代码的情况下),尽量少地调用.SaveChanges()

与每个更改调用50次相比,一次调用50次更改通常更好/更快/更有效。


3
新手问题 - 假设我在循环中创建了10个实体 - 其中9个是“有效”的,而一个不是(例如,Id = null) - 循环后调用SaveChanges()不会将任何实体保存到数据库中 - 这是正确的吗? 谢谢任何人的澄清。 - fxdx
3
是的,你说得对 - 因为至少有一个错误(无论是验证还是其他方面),整个 .SaveChanges() 操作都会失败,并且不会将任何内容存储到数据库中。 - marc_s
1
@marc_s 所以,如果因为1000个实体中的1个而导致错误,最好的处理方式是什么? - Faizan khan
2
@marc_s - 如果我在循环中为每个项目发送电子邮件,那么如果我将savechanges放在循环外面,即使在savechanges中出现问题,电子邮件也会每次发送。如何处理这种情况? - Neel
@Neel 我现在也有同样的问题/情况。你是怎么解决的?我在考虑为这种情况创建一个单独的函数,意思是不重用一个为单个实体设计的函数(我正在构建批量插入用户)。 - undefined

13

还有一件事需要说明,如果你的foreach循环遍历了在你的DbContext中处于活动状态的EntitySet,那么你将会收到一个System.Data.SqlClient.SqlException异常:另一个线程正在该会话中运行。

这是因为foreach实际上在另一个线程中运行,而事务不允许完成这种操作。

除了以上提到的尽量减少事务次数的建议外,还需注意这个问题。

假设你有一个名为db的context并执行以下操作:

var groups = from i in db.items select i.GroupNumber;
foreach( var grp in groups)
{
    //... do something
    db.SaveChanges();
}

这将抛出异常

避免这种情况的一种解决方案(如果适用!)是通过将它们带入对象空间来实现“实体组”的“实质化”,方法是将第一行更改为:

var groups = (from i in db.items select i.GroupNumber).ToList();

这将允许您在foreach中保存(如果需要)


3

在循环外只会创建一个事务来更新数据库,而在循环内每次迭代都会创建一个事务。因为EF使用了工作单元模式,它将所有的更改保存在内存中,在SaveChanges方法中将所有更改保存到数据库中。


谢谢你的快速回复!那么在循环外部实际上更快,还是性能优势相当微小? - jaffa
1
是的,在大多数情况下,它会提供更好的性能。除非您有一个迭代的长时间运行工作(可能是一些重型IO处理)。 - Jayantha Lal Sirisena

3

性能会有所不同,在循环内部,您将在每次迭代中调用数据库。在循环外部,您将只调用一次数据库。这可能会对性能产生巨大影响,具体取决于您有多少次迭代。


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