2017-09-29 46 views
3

我正在研究实用程序来读取我已经给出的JSON文件并将其转换为SQL Server。我选择的武器是一个.NET Core控制台应用程序(我试图用.NET Core来完成我所有的新工作,除非有令人信服的理由)。我把所有的东西都“工作”了,但是在某个地方显然存在问题,因为这些表现真的很可怕,几乎无法使用。JSON数组到实体框架核心非常慢?

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(); 
} 
+0

LINQ的-2-SQL是另一种选择。问题可能是它在后端对数据库进行单独插入。你打开EntityFramework的日志记录功能吗?只需连线到'Console.Out' –

+0

哪一行是最慢的?你是否试图用调试器来发现它? –

+0

你说甚至没有调用'SaveChanges'也很慢?如何评论所有'_db.Entry(...'行?仍然很慢? –

回答

2

我有这样的代码,大量的循环和吨变化的状态问题。

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

对我来说,修正是在一些关键点上创建新的EF上下文(_db)。它为我节省了几个小时的运行时间!

你可以尝试在此循环

创建_db每次迭代的新实例,包含214元

的主阵列如果作出任何改变,尝试一些stopwatch增加得到什么/在哪里需要这么长时间的最佳想法。

+0

热潮! 24小时以上不到24分钟!我删除了用于Db上下文的使用块,并在外部for循环内部实例化它,并在调用Save Changes()后立即将其放置在最后。谢啦! –

+0

很高兴帮助:) – Iannick

0

如果您要进行数千次更新,那么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); 
}