【问题标题】:JSON Array to Entity Framework Core VERY Slow?JSON 数组到实体框架核心非常慢?
【发布时间】:2023-03-16 13:32:01
【问题描述】:

我正在开发一个实用程序来读取我收到的 JSON 文件并将其转换为 SQL Server。我选择的武器是 .NET Core 控制台应用程序(我正在尝试使用 .NET Core 完成所有新工作,除非有令人信服的理由不这样做)。我的整个东西都在“工作”,但显然某个地方存在问题,因为性能真的很可怕,几乎到了无法使用的地步。

JSON 文件大约 27MB,包含一个由 214 个元素组成的主数组,每个元素包含几个字段以及一个包含 150-350 条记录的数组(该数组有几个字段,可能还有一个小于 5 条记录的小数组或两个)。总记录约为 35,000 条。

在下面的代码中,我更改了一些名称并删除了一些字段以使其更具可读性,但所有实际工作的逻辑和代码都没有改变。

请记住,我已经对 SaveChanges() 的位置和调用次数进行了大量测试,最初认为访问 Db 的次数是问题所在。尽管下面的版本为 214 条记录循环的每次迭代调用一次 SaveChanges(),但我尝试将它移到整个循环结构之外,并且性能没有明显变化。换句话说,在零次到 Db 的情况下,这仍然是缓慢的。你问有多慢,> 24 小时运行对你有什么影响?在这一点上,我愿意尝试任何事情,甚至考虑将整个过程转移到 SQL Server 中,但在 C# 中工作要比在 TSQL 中工作要好得多。

static void Main(string[] args)
{
    string statusMsg = String.Empty;

    JArray sets = JArray.Parse(File.ReadAllText(@"C:\Users\Public\Downloads\ImportFile.json"));
    try
    {
        using (var _db = new WidgetDb())
        {
            for (int s = 0; s < sets.Count; s++)
            {
                Console.WriteLine($"{s.ToString()}: {sets[s]["name"]}");

                // First we create the Set
                Set eSet = new Set()
                {
                    SetCode = (string)sets[s]["code"],
                    SetName = (string)sets[s]["name"],
                    Type = (string)sets[s]["type"],
                    Block = (string)sets[s]["block"] ?? ""
                };
                _db.Entry(eSet).State = Microsoft.EntityFrameworkCore.EntityState.Added;

                JArray widgets = sets[s]["widgets"].ToObject<JArray>();
                for (int c = 0; c < widgets.Count; c++)
                {
                    Widget eWidget = new Widget()
                    {
                        WidgetId = (string)widgets[c]["id"],
                        Layout = (string)widgets[c]["layout"] ?? "",
                        WidgetName = (string)widgets[c]["name"],
                        WidgetNames = "",
                        ReleaseDate = releaseDate,
                        SetCode = (string)sets[s]["code"]
                    };

                    // WidgetColors
                    if (widgets[c]["colors"] != null)
                    {
                        JArray widgetColors = widgets[c]["colors"].ToObject<JArray>();

                        for (int cc = 0; cc < widgetColors.Count; cc++)
                        {
                            WidgetColor eWidgetColor = new WidgetColor()
                            {
                                WidgetId = eWidget.WidgetId,
                                Color = (string)widgets[c]["colors"][cc]
                            };
                            _db.Entry(eWidgetColor).State = Microsoft.EntityFrameworkCore.EntityState.Added;
                        }
                    }

                    // WidgetTypes
                    if (widgets[c]["types"] != null)
                    {
                        JArray widgetTypes = widgets[c]["types"].ToObject<JArray>();

                        for (int ct = 0; ct < widgetTypes.Count; ct++)
                        {
                            WidgetType eWidgetType = new WidgetType()
                            {
                                WidgetId = eWidget.WidgetId,
                                Type = (string)widgets[c]["types"][ct]
                            };
                            _db.Entry(eWidgetType).State = Microsoft.EntityFrameworkCore.EntityState.Added;
                        }
                    }

                    // WidgetVariations
                    if (widgets[c]["variations"] != null)
                    {
                        JArray widgetVariations = widgets[c]["variations"].ToObject<JArray>();

                        for (int cv = 0; cv < widgetVariations.Count; cv++)
                        {
                            WidgetVariation eWidgetVariation = new WidgetVariation()
                            {
                                WidgetId = eWidget.WidgetId,
                                Variation = (string)widgets[c]["variations"][cv]
                            };
                            _db.Entry(eWidgetVariation).State = Microsoft.EntityFrameworkCore.EntityState.Added;
                        }
                    }
                }
                _db.SaveChanges();
            }
        }

        statusMsg = "Import Complete";
    }
    catch (Exception ex)
    {
        statusMsg = ex.Message + " (" + ex.InnerException + ")";
    }

    Console.WriteLine(statusMsg);
    Console.ReadKey();
} 

【问题讨论】:

  • Linq-2-Sql 是另一种选择。问题可能是它在后端对数据库进行单独插入。您是否打开了 EntityFramework 的日志记录功能?只需将其连接到Console.Out
  • 哪一行最慢?您是否尝试使用调试器找出答案?
  • 你说即使不打电话SaveChanges也很慢?注释掉所有_db.Entry(... 行怎么样?还是很慢?
  • 还有更多的调整选项,例如设置_db.ChangeTracker.AutoDetectChangesEnabled = false;,或在每次循环迭代时使用上下文实例。
  • 查看 EFC 实现,关键是关闭更改跟踪(@GertArnold 提到的第一个选项)。对于这样的数据量,即使是 24 分钟也太多了。这样的时间表示二次复杂度算法,这正是更改跟踪开启时发生的情况 - 每个Entry 调用都会检查每个当前跟踪的实体ChangeTracker.DetectChanges()。顺便说一句,使用自然的Add 方法比使用Entry(..).State = Added 更好。

标签: c# json entity-framework json.net .net-core


【解决方案1】:

我对这种代码有问题,有很多循环和大量的状态变化。

您在 _db 上下文中所做的任何更改/操作都会生成它的“跟踪”。它每次都会让你的上下文变慢。阅读更多here

对我来说,解决方法是在某些关键点创建新的 EF 上下文(_db)。每次运行它为我节省了几个小时!

您可以尝试在此循环中的每次迭代中创建一个新的 _db 实例

包含一个由 214 个元素组成的主数组

如果没有改变,请尝试添加一些 stopwatch 以更好地了解什么/哪里花了这么长时间。

【讨论】:

  • 轰隆隆! 24 小时以上到不到 24 分钟!我删除了 Db 上下文的 using 块,并在外部 for 循环内对其进行了实例化,并在调用 Save Changes() 之后将其丢弃。谢谢大佬!
  • 很高兴帮助:)
【解决方案2】:

如果您要进行数千次更新,那么 EF 并不是真正可行的方法。 SQLBulkCopy 之类的东西可以解决问题。

你可以试试bulkwriter 库。

IEnumerable<string> ReadFile(string path)  
{
using (var stream = File.OpenRead(path))
     using (var reader = new StreamReader(stream))
    {
        while (reader.Peek() >= 0)
       {
              yield return reader.ReadLine();
       }
    }
}

var items =  
    from line in ReadFile(@"C:\products.csv")
    let values = line.Split(',')
    select new Product {Sku = values[0], Name = values[1]};

然后

using (var bulkWriter = new BulkWriter<Product>(connectionString)) {  
     bulkWriter.WriteToDatabase(items);
}

【讨论】:

    猜你喜欢
    • 2023-02-26
    • 1970-01-01
    • 2019-02-27
    • 1970-01-01
    • 1970-01-01
    • 2018-07-08
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多