2014-01-22 134 views
15

最近,我正在做一些簡單的EF工作。很簡單, 第一,實體框架性能問題,saveChanges很慢

List<Book> books = entity.Books.WHERE(c=>c.processed==false)ToList(); 
then 
    foreach(var book in books) 
    { 
     //DoSomelogic, update some properties, 
    book.ISBN = "ISBN " + randomNumber(); 
    book.processed = true; 
    entity.saveChanges(book) 
    } 

我把entity.saveChangesforeach內,因爲它是一個大名單,100k左右的記錄,如果這個記錄是沒有問題的處理,然後將此記錄,設置book.processed =真,如果進程被異常打斷,那麼下一次,我不必再處理這些好記錄。

一切似乎都對我好。處理數百條記錄時速度很快。然後當我們移動到10萬條記錄時,entity.saveChanges非常慢。每個記錄大約1-3秒。然後我們保留實體模型,但用經典SqlHelper.ExecuteNonQuery("update_book", sqlparams)替換entity.saveChanges。而且速度非常快。

誰能告訴我爲什麼實體框架過程那麼慢?如果我仍然想使用entity.saveChanges,那麼提高性能的最佳方法是什麼?

謝謝

回答

22

在執行插入操作之前,請關閉更改跟蹤。這會顯着提高您的性能(大小順序)。將SaveChanges()放在循環外部也會有所幫助,但關閉更改跟蹤將有更多幫助。

using (var context = new CustomerContext()) 
{ 
    context.Configuration.AutoDetectChangesEnabled = false; 

    // A loop to add all your new entities 

    context.SaveChanges(); 
} 

有關更多信息,請參閱this page

+0

是的,固定的,謝謝:) – Steve

+11

什麼是將此關閉的負面影響? – RayLoveless

+0

@RayLoveless'SaveChanges'將提交每個插入實體中的每個字段到數據庫。查詢將會更長,但可以說比運行'DetectChanges'還要快,特別是對於批量插入。 – Gusdor

4

我會將SaveChanges(book)放在foreach之外。由於本書作爲列表在實體上,因此您可以將其放在外面,並且EF可以更好地使用生成的代碼。

列表是實體上的一個屬性,EF設計用於優化後端數據庫上的更新/創建/刪除。如果你這樣做,我會好奇它是否有幫助。

+0

我把savechanges放在foreach的原因是因爲記錄相對較大,運行時間很昂貴(通常需要4-5小時)。所以我們決定更新是否沒有問題,我們將此記錄標記爲「已處理」。所以如果它崩潰或發生任何事情,我們不必再次重新更新「好」記錄。 – user454232

+0

這是一個很好的想法,但Entity Framework可能會以其他方式更好地處理它。實體框架能夠優化更新和插入。當savechanges()被放置在一個循環中時,它也傾向於在整個記錄上嘗試完全提交。 –

0

AsNoTracking」 爲我的作品

例如:

Item itemctx = ctx.Items.AsNoTracking().Single(i=>i.idItem == item.idItem); 
ctx.Entry(itemctx).CurrentValues.SetValues(item); 
itemctx.images = item.images; 
ctx.SaveChanges(); 

沒有 「AsNoTracking」 更新很慢。

+0

obs:這種方法不更新孩子 – Paulo

0

在我看來,Entity Framework對於BULK操作來說是一個糟糕的選擇,無論從性能還是內存消耗的角度來看。一旦你超過了幾千條記錄,SaveChanges方法真的開始崩潰。

您可以嘗試將您的工作劃分爲較小的交易,但我認爲您正在努力工作以創建此項目。

更好的方法是利用已由您的DBMS提供的BULK操作。 SQL Server通過.NET提供BULK COPY。 Oracle爲Oracle.DataAccess或非託管數據訪問提供了BULK COPY。對於Oracle.ManagedDataAccess,不幸的是BULK COPY庫不可用。但是我可以使用BULK COLLECT/FOR ALL來創建一個Oracle存儲過程,這使我可以在幾秒鐘內插入數千條記錄,並且在應用程序中佔用的內存要少得多。在.NET應用程序中,您可以實現PLSQL關聯數組作爲參數等。

利用DBMS中的BULK工具的好處是減少了應用程序,查詢處理器和數據庫引擎之間的上下文切換。

我確定其他數據庫供應商提供類似的東西。

1

我也可能會建議您將SaveChanges()從循環中取出,因爲它會對數據庫執行n次更新,因此上下文將有n次迭代通過檢查點和驗證。

var books = entity.Books.Where(c => c.processed == false).ToList(); 

books.Foreach(b => 
{ 
    b.ISBN = "ISBN " + randomNumber(); 
    b.processed = true; 
    //DoSomelogic, update some properties 
}); 
entity.SaveChanges();