2010-12-04 53 views
26

我必須從一個CSV文件導入約30k行到我的SQL數據庫,這可悲的是需要20分鐘。如何加快DbSet.Add()?

使用Profiler進行故障診斷表明DbSet.Add佔用了大部分時間,但是爲什麼?

我有這些實體框架代碼優先類:

public class Article 
{ 
    // About 20 properties, each property doesn't store excessive amounts of data 
} 

public class Database : DbContext 
{ 
    public DbSet<Article> Articles { get; set; } 
} 

對於每一個項目在我的for循環我做的:

db.Articles.Add(article); 

外for循環我做的:

db.SaveChanges(); 

它與我的本地SQLExpress服務器連接,但我猜想沒有任何東西寫到Sav eChanges被稱爲所以我猜服務器不會是問題....

+1

你好。你是否擺脫了實體框架或與EF一起使用了sqlbulkcopy?我得到了完全相同的問題.Add() – 2011-03-29 08:10:23

+6

如果你設置這些: `db.Configuration.ValidateOnSaveEnabled = false; db.Configuration.AutoDetectChangesEnabled = false;` 有一個巨大的性能增益。你必須確保你的價值觀堅韌。 – 2011-03-29 11:19:10

+0

在註釋中使用反引號(`)作爲代碼。看起來很有趣,我稍後會研究這些屬性... – 2011-03-29 11:24:27

回答

8

工作單元中的每個項目都有開銷,因爲它必須檢查(和更新)身份管理器,添加到各種集合等等。

我會嘗試的第一件事就是批量進入500個組(比如更改該數字以適應),每次都從新的(新)對象上下文開始 - 否則您可以合理地期望伸縮性能。把它分成批次也可以防止巨石交易把所有事情都停下來。

除此之外; SqlBulkCopy的。它專爲大量進口而設計,開銷最小。儘管這不是EF。

+0

如果適用於您的設計,我一定會用SqlBulkCopy。 – 2010-12-04 21:10:33

+0

我正試圖用這個來完成一些事情,但我不知道它是否會接受基於列名稱而不是其屬性的匹配... – 2010-12-04 21:26:51

1

我還沒有真正嘗試過這一點,但我的邏輯是堅持ODBC驅動程序加載文件到數據表,然後使用SQL存儲過程將表傳遞給過程。

在第一部分,嘗試: http://www.c-sharpcorner.com/UploadFile/mahesh/AccessTextDb12052005071306AM/AccessTextDb.aspx

對於第二部分嘗試一下本作SQL過程: http://www.builderau.com.au/program/sqlserver/soa/Passing-table-valued-parameters-in-SQL-Server-2008/0,339028455,339282577,00.htm

而在C#創建SqlCommnand對象,並添加到它的參數集合的SqlParameter是SqlDbType。結構化

嗯,我希望它有幫助。

16

我會說,如果你只是做插入(沒有更新或刪除),那麼你可以在一般情況下,安全地做任何插入前將以下屬性添加到Kervin拉麪的評論在上下文中:

DbContext.Configuration.AutoDetectChangesEnabled = false; 
DbContext.Configuration.ValidateOnSaveEnabled = false; 

我在工作中遇到了一次性批量導入問題。如果不設置上述屬性,則在上下文中添加大約7500個複雜對象需要花費30多分鐘。設置上述屬性(如此禁用EF檢查並更改跟蹤)將導入減少到秒。

但是,我再次強調只有在插入時才使用它。如果您需要將插入與更新/刪除混合在一起,您可以將代碼拆分爲兩個路徑,並禁用插入部分的EF檢查,然後重新啓用更新/刪除路徑的檢查。我已經成功地使用這種方法來解決緩慢的DbSet.Add()行爲。

4

有一個非常容易使用和非常快速的延伸位置: https://efbulkinsert.codeplex.com/

這就是所謂的「實體框架批量插入」。

擴展本身位於名稱空間EntityFramework.BulkInsert.Extensions中。因此,要揭示擴展方法添加使用

using EntityFramework.BulkInsert.Extensions; 

然後你就可以順便說一句這樣做

context.BulkInsert(entities); 

- 如果你不希望使用這個擴展出於某種原因,你也可以嘗試運行,而不是每篇文章的db.Articles.Add(文章),每次創建幾篇文章的列表,然後使用AddRange(EF版本6中的新增內容,以及RemoveRange)將它們一起添加到dbcontext中。