首页 > 解决方案 > C# .NET 4.6.1 Entity Framework - DB.MyTable.Add(...) 很慢,尽管没有调用 DB.SaveChanges()

问题描述

(A)这个版本很......持续时间以分钟为单位测量

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)文件

根据DbContext.Add文档

开始跟踪给定实体以及任何其他尚未被跟踪的可到达实体,处于已添加状态,以便在调用 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;
}

        

标签: c#entity-framework

解决方案


如评论中所述 - 实体框架(不确定实体框架核心)默认调用DetectChanges每个Add. 除其他外,此功能会扫描已被上下文跟踪的所有实体,以检测它们内部和之间的变化。这意味着该函数的时间复杂度为 O(n),其中 n 是上下文已跟踪的实体数。当您在循环中进行大量添加时 - 时间复杂度变为 O(n^2),其中 n 是添加的项目总数。因此,即使只有 3000 行这样的小数字,性能也会显着下降。

要解决这个(有争议的设计)问题,有几个选项:

  1. 将上下文的 AutoDetectChangesEnabled 设置为false. 然后手动调用DetectChanges之前SaveChanges

  2. 或使用AddRange而不是逐个添加实体,它只调用DetectChanges一次。

另一个注意事项:

  1. 尽量避免在操作之间重用上下文。你说在你调用第一个 Add 之前已经有 3000 个实体被上下文跟踪。每次需要时最好创建新的上下文,做这些事情,然后处理它。性能影响可以忽略不计(并且连接由连接池管理,每次创建\处置上下文时都不需要打开或关闭),但是像这样的问题会少得多(重用上下文不仅会在您现在有,但在其他几个)。

  2. AsNoTracking如果您不打算修改特定查询返回的实体(或者如果您打算稍后通过附加到上下文来修改其中一些实体),请使用查询。然后上下文将不会跟踪它们,这将减少提及和其他性能问题的可能性。

至于 Linq To Sql - 它具有类似的“检测更改”概念,但仅在将更改提交到数据库之前自动调用,而不是在每次添加时自动调用,因此您不会在那里看到相同的问题。


推荐阅读