2016-05-31 441 views
1

我有一個控制檯線應用程序,通過LINQ將大約150,000行保存到數據庫。這工作正常(我通常會期望它停止工作)。這是一個沼澤標準保存從CSV文件中讀取數據後進行更改來電: -LINQ SaveChanges()異常緩慢

List<Invoice> oldInvoices = db.Invoices.Where(x => !x.IsVisible).ToList(); 
List<int> oldInvoiceIDs = oldInvoices.Select(s => s.InvoiceID).ToList(); 

List<InvoiceProduct> allInvoiceProducts = db.InvoiceProducts.ToList(); 
List<InvoiceProduct> oldInvoiceProducts = allInvoiceProducts.Where(x => oldInvoiceIDs.Contains(x.InvoiceID)).ToList(); 

db.InvoiceProducts.RemoveRange(oldInvoiceProducts); 
db.Invoices.RemoveRange(oldInvoices); 

UpdateConsole.WriteLine("Switching over invoices completed. Please wait...", ConsoleColor.Black, ConsoleColor.Magenta); 

表是用的產品對每張發票子鏈接表的發票清單。每次我們獲取新數據時,我們都會寫入新數據,在數據庫中將其標記爲不可見,然後將當前可見數據切換爲不可見,並將當前不可見數據切換爲可見數據,從而可以立即切換從一個數據集到下一個數據集。剛剛被標記爲不可見的數據集然後通過LINQ被刪除。

這需要時間來刪除,但不是不合理的時間量。由於此數據來自CSV數據文件,因此我們記錄了行數以及文件讀取的開始和結束日期和時間。這是存儲在另一個數據庫表和代碼保存爲: -

importLog.SuccessfullyImportedRows = successfulRows; 
importLog.FailedImportedRows = failedRows; 
importLog.EndTime = DateTime.Now; 

db.SaveChanges(); 

這保存,超過40分鐘需要和我不知道爲什麼。我能想到的唯一的事情就是它使用了在Visual Studio中生成EDMX時可用的相同的DBEntities類。

有沒有人有這個?它給應用程序掛的外觀,但它確實持續40分鐘打完...

+0

可能重複[如何更快地運行此任務](http://stackoverflow.com/questions/37374480/how-can-i-run-this-task-faster) – Veverke

+0

可能會幫助您http:// stackoverflow.com/questions/37096509/why-getting-data-with-entity-framwork-is-slow – mohsen

+0

感謝您的鏈接。後者是關於循環內部的SaveChanges(),而我並不是。還值得注意的是,當我保存177,000條記錄時,我保存到本地列表中,然後執行AddRange()和SaveChanges()。我只調用SaveChanges()一次保存列表,第二次保存日誌。沒有任何循環的SaveChanges(),相同的問題仍然存在嗎? –

回答

1

您的辦法多的性能問題:

  1. 從數據庫中拖動不必要的數據。
  2. 批量插入巨大的記錄。
  3. 從Hude Records刪除批量。

沒有必要將所有的發票從數據庫然後在內存中,在那裏你可以直接查詢這些數據庫和retreive只有你想要的清單本地過濾。

您需要更換此:

List<InvoiceProduct> allInvoiceProducts = db.InvoiceProducts.ToList(); 
List<InvoiceProduct> oldInvoiceProducts = allInvoiceProducts.Where(x => oldInvoiceIDs.Contains(x.InvoiceID)).ToList(); 

有:

List<InvoiceProduct> oldInvoiceProducts = db.InvoiceProducts.Where(x => oldInvoiceIDs.Contains(x.InvoiceID)).ToList(); 

對於批量刪除更快的方法:

String commaDelimitedIds = String.Join(",", oldInvoiceIDs); 
String query = "DELETE FROM Invoice WHERE InvoiceID IN (" + commaDelimitedIds + ")"; 
db.ExecuteQuery(query); 

通過Linq To SQL插入150,000 recod是不是一個好主意,這將產生150,000Insert聲明(否提及關係對象)。

看看這個例子:SQLBulkCopy這是非常適合巨大的插入。

一般來說,ORMs 對於批量操作不是一個好主意。

+0

嗨,謝謝你的回覆。你說我從數據庫中拖出不必要的數據是正確的。我之前遇到的問題是,「with:」之後的第三行代碼檢索到錯誤,因爲.Contains()在列表中有太多整數。因此,我選擇了一個額外的行中的本地列表,然後做了我上面的方法。非常感謝您的幫助!我會放棄它。 –

+1

@MikeUpjohn您可以使用for循環將每個2000 ID拆分在一起,最後將Concat列表一起分割。這是SQL參數限制的解決方法。 – user3185569

1

首先,在您的查詢中,我看到使用.toList()時出現問題。 toList意味着你強制該查詢立即運行並將其存儲到內存中。對於小數據來說速度更快,但對於超過150,000行來說,你肯定會遇到性能問題,並且會因爲內存不足而出現問題。您可以使用AsQueryable()而不是。

AsQueryable只是創建一個查詢,獲取 列表所需的說明。您可以在稍後對查詢進行進一步更改,例如將新的Where子句添加到 級別的 級別。

對於EF 6或更高版本,RemoveRange的性能非常快。所以我不認爲RemoveRange是這裏的根本原因。但是,如果您想提高性能,請嘗試使用此擴展名。這太好了。 https://efbulkinsert.codeplex.com/

0

好吧,我找到的解決方案(雖然不知道推理)。如果我不走實體項目納入日誌記錄功能,我從EDMX產生實體的新實例e.g.:-

using(DBEntities db = new DBEntities()) { 
    importLog.SuccessfullyImportedRows = successfulRows; 
    importLog.FailedImportedRows = failedRows; 
    importLog.EndTime = DateTime.Now; 

    db.SaveChanges(); 
} 

這個工作在不到一秒鐘。在插入如此多行的DBEntities的原始實例中緩存了某些內容?