c# - C# .NET 4.6.1 Entity Framework - DB.MyTable.Add(...) 很慢,尽管没有调用 DB.SaveChanges()
问题描述
(A)这个版本很慢......持续时间以分钟为单位测量
- DB 是 SQL 服务数据库的典型 EF 数据上下文
AA_Words_100
是添加到 EF 设计器的简单 SQL Server 表DB.AA_Words_100.Add
被调用约 3,000 次(通过使用计数器变量进行调试确认)- 我已经确认 >99% 的运行时间在内部循环中
XCampaign
是一个Dictionary<string, Dictionary<string, _Word>>
其中 _Word 是一个普通的非 EF 对象。
foreach (var XCampaign in Words100)
foreach (var KVP in XCampaign.Value)
DB.AA_Words_100.Add(KVP.Value.To_AA_Word_100());
DB.SaveChanges();
(B)这个版本很快...... -.Add()
只是为了缩小范围而注释掉
var iTemp = 0;
foreach (var XCampaign in Words100)
foreach (var KVP in XCampaign.Value)
iTemp++;
DB.SaveChanges();
(C)这个版本很快。我只是在打电话之前填写了一个清单DB.AddRange(...)
var LIST_WordsToAdd = new List<AA_Words_100>();
foreach (var XCampaign in Words100)
{
foreach (var KVP in XCampaign.Value)
{
LIST_WordsToAdd.Add(KVP.Value.To_AA_Word_100());
}
DB.AA_Words_100.AddRange(LIST_WordsToAdd);
}
DB.SaveChanges();
(D)文件
开始跟踪给定实体以及任何其他尚未被跟踪的可到达实体,处于已添加状态,以便在调用 SaveChanges() 时将它们插入数据库。
特别是何时SaveChanges()
被调用。
我最近在此应用程序中从 Linq-to-SQL 迁移到 EF。Linq-to-SQL 没有这个问题。
DB.AA_Words_100.Add(...)
命令这么慢的原因是什么?
谢谢!
#Update - To_AA_Word_11() 代码
public AA_Words_100 To_AA_Word_100()
{
var W = new AA_Words_100();
W.Word = Word;
W.Word_Clean = Word.Replace("'", "");
W.PhraseCount = PhraseCount;
W.Clicks = Clicks;
W.Impressions = Impressions;
W.SalesAmt = SalesAmt;
W.SalesOrders = SalesOrders;
W.SalesUnits = SalesUnits;
W.Spend = Spend;
W.Campaign = Campaign;
return W;
}
解决方案
如评论中所述 - 实体框架(不确定实体框架核心)默认调用DetectChanges
每个Add
. 除其他外,此功能会扫描已被上下文跟踪的所有实体,以检测它们内部和之间的变化。这意味着该函数的时间复杂度为 O(n),其中 n 是上下文已跟踪的实体数。当您在循环中进行大量添加时 - 时间复杂度变为 O(n^2),其中 n 是添加的项目总数。因此,即使只有 3000 行这样的小数字,性能也会显着下降。
要解决这个(有争议的设计)问题,有几个选项:
将上下文的 AutoDetectChangesEnabled 设置为
false
. 然后手动调用DetectChanges
之前SaveChanges
。或使用
AddRange
而不是逐个添加实体,它只调用DetectChanges
一次。
另一个注意事项:
尽量避免在操作之间重用上下文。你说在你调用第一个 Add 之前已经有 3000 个实体被上下文跟踪。每次需要时最好创建新的上下文,做这些事情,然后处理它。性能影响可以忽略不计(并且连接由连接池管理,每次创建\处置上下文时都不需要打开或关闭),但是像这样的问题会少得多(重用上下文不仅会在您现在有,但在其他几个)。
AsNoTracking
如果您不打算修改特定查询返回的实体(或者如果您打算稍后通过附加到上下文来修改其中一些实体),请使用查询。然后上下文将不会跟踪它们,这将减少提及和其他性能问题的可能性。
至于 Linq To Sql - 它具有类似的“检测更改”概念,但仅在将更改提交到数据库之前自动调用,而不是在每次添加时自动调用,因此您不会在那里看到相同的问题。
推荐阅读
- php - 使用 exec() 时,PHP Sudo 权限仅适用于“所有”命令
- python - 计算椭圆(来自 fitellipse 函数 opencv)和直线(具有起点和终点)之间的交点
- cypress - 赛普拉斯重新加载页面直到元素可见
- laravel - 在 Laravel 请求中删除段或更改 REQUEST_URI?
- postgresql - 在 postgres 中导入 CSV 文件时的错误控制
- ios - 如何在 iPad 上使用 CMPedometer?
- gekko - 当我使用较少的 nlp 迭代时,APOPT 正在为 MINLP 问题找到更好的局部最小值。我期待相反的结果,我错过了什么?
- javascript - 无法使用 selenium 的类实现来抓取网页
- reactjs - 防止 FullCalendar (React) 在每次渲染时重新获取事件和资源
- delphi - 使用已加载的文本独立更改 RichEdit 文本颜色