2017-03-31 39 views
-2

我允许用户将一些数据下载到csv。然后,他们可以编辑一些列,然后将其上传。我需要一种快速有效的方式来比较相似对象之间的某些列,以查看更改的内容。比较2列表对象的最快方法

目前笔者从DB拉的原始数据,并使它成为一个列表,所以这一切都在内存中。大约有10万个物品,所以没那么糟糕。这部分不到一秒钟。然后我加载到csv文件并将其放入列表中。两个列表都具有相同的类类型。

然后我遍历CSV数据(因为他们可能删除了一些行,他们并没有改变,但他们仍然可以有很大的变化行)。对于csv列表中的每一行,我查询来自数据库的列表以查找该对象。现在我将csv对象和数据库中的对象作为相同的结构。然后,我通过自定义对象比较函数运行它,查看某些列以查看是否有任何更改。

如果事情的确发生了变化我要验证,他们进入的是通过查询一个有效的值该列另一个参考列表。如果无效,我将它写入例外列表。最后如果没有例外,我保存到db。如果有例外,我不保存任何内容,并向他们显示错误列表。

细节比较提供的列清单和老VS,改变了新的价值。我需要这个来查询引用列表,以确保新值在我进行更改之前是有效的。这是相当低效的,但它给用户提供了非常有价值的可能是上传问题的详细信息。

这是非常缓慢的。我正在寻找加快速度的方法,同时仍然能够向用户提供有关它为什么可能失败的详细信息,以便他们能够纠正它。

// get all the new records from the csv 
      var newData = csv.GetRecords<MyTable>().ToArray(); 

      // select all data from database to list 
      var origData = ctx.MyTable.Select(s => s).ToList(); 

      // look for any changes in the new data and update the database. note we are looping over the new data so if they removed some data from the csv file it just won't loop over those and they won't change 
      foreach (var d in newData) 
      { 
       // find data so we can compare between new (csv) and current (from db) to see what possibly changed 
       var oData = (from o in origData 
          where o.id == d.id 
          select o).FirstOrDefault(); 

       // only the columns in the updatableColumns list are compared 
       var diff = d.DetailedCompare(oData, comparableColumns.ToList()); 
       if (diff.Count > 0) 
       { 
        // even though there are differences between the csv record and db record doesn't mean what the user input is valid. only existing ref data is valid and needs to be checked before a change is made 
        bool changed = false; 

        // make a copy of this original data and we'll check after if we actually were able to make a change to it (was the value provided valid) 
        var data = CopyRecord(oData); 

        // update this record's data fields that have changed with the new data 
        foreach (var v in diff) 
        { 
         // special check for setting a value to NULL as its always valid to do this but wouldn't show up in ref data to pass the next check below 
         if (v.valA == null) 
         { 
          oData.GetType().GetProperty(v.Prop).SetValue(oData, v.valA); 
          oData.UpdatedBy = user; 
          oData.UpdatedDate = DateTime.Now; 
          changed = true; 
         } 
         // validate that the value for this column is in the ref table before allowing an update. note exception if not so we can tell the user 
         else if (refData[v.Prop].Where(a => a.value == v.valA.ToString()).FirstOrDefault() != null) 
         { 
          // update the current objects values with the new objects value as it changed and is a valid value based on the ref data defined for that column 
          oData.GetType().GetProperty(v.Prop).SetValue(oData, v.valA); 
          oData.UpdatedBy = user; 
          oData.UpdatedDate = DateTime.Now; 
          changed = true; 
         } 
         else 
         { 
          // the value provided isn't valid for this column so note this to tell the user 
          exceptions.Add(string.Format("Error: ID: {0}, Value: '{1}' is not valid for column [{2}]. Add the reference data if needed and re-import.", d.id, v.valA, v.Prop)); 
         } 
        } 

        // we only need to reattach and save off changes IF we actually changed something to a valid ref value and we had no exceptions for this record 
        if (changed && exceptions.Count == 0) 
        { 
         // because our current object was in memory we will reattached it to EF so we can mark it as changed and SaveChanges() will write it back to the DB 
         ctx.MyTable.Attach(oData); 
         ctx.Entry(oData).State = EntityState.Modified; 

         // add a history record for the change to this product 
         CreateHistoryRecord(data, user); 
        } 
       } 
      } 

      // wait until the very end before making DB changed. we don't save anything if there are exceptions or nothing changed 
      if (exceptions.Count == 0) 
      { 
       ctx.SaveChanges(); 
      } 
+0

将新数据插入临时表并使用SQL过滤原始数据? –

回答

4

的第一个大胜利将是把你的数据在一个字典,以便您可以通过ID获得所需的值迅速,而不必通过成千上万个对象的搜索对象。我很确定它会更快。

除此之外,我建议你通过一个分析器运行您的代码,以确定到底是哪部分是最慢的。 DetailedCompare()完全可能做的非常缓慢,但可能并不明显。

+0

您是否认为首先将2个列表连接在一起,其中select是每1列有2列的对象,因此我们需要一个具有id然后column_a_new,column_a_old等的对象,然后遍历该一个连接列表只要?这将消除从数据库列表中查找值的需求完全正确吗?或者,如果我对字典进行了更改,它也会删除该查找。 – user441521

+0

是的,我相信这些解决方案中的任何一个都可以执行相同数量的操作。但正如我所说,最好的方法是使用探查器,即使是Visual Studio中内置的探查器也会有所帮助(我相信这就是所谓的性能探索器)。 –

+0

Profiler说这两行是最慢的:ctx.MyTable.Attach(oData); ctx.Entry(oData).State = EntityState.Modified;这很糟糕,因为这就是我如何将数据返回到数据库以便它们可以使用SaveChanges进行更新。 – user441521

0

有一点需要考虑的是异步比较和/或异步if (diff,Count > 0)至少后者假设有几个随机变化为什么要等待所有的复制和反射。把它放在分隔功能中并且平行运行。

+0

EF似乎不是线程安全的。我会看到我可以如何重组这个平行部分不会以某种方式击中EF。我完全忘记了并行编程,所以这看起来很有前景,因为当我评论EF这个东西时这很快。 – user441521