2017-03-31 67 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