2013-06-12 57 views
9

當我使用我的xxxContext對象並向表中添加多個添加時,SaveChanges()實體框架如何將其解析爲SQL?它只是循環做插入到xxx或者如果有數百行,是否足夠聰明地發出大容量插入命令?.NET實體框架插入與批量插入

紅利問題:如果它沒有發出大容量插入有強制它的方式,所以我的數據庫性能不會被單獨的插入殺死?或者批量到臨時表然後像Upsert合併到原始表中?

+1

使用的SqlBulkCopy的API – ErikEJ

+0

可能重複[實體框架中插入的最快的方法(http://stackoverflow.com/questions/5940225/fastest-way-of-inserting-in-entity-framework) –

+0

實體框架對此非常緩慢。找到了一種更快的方法。 基本上你會批量插入臨時表,然後從那裏發出一個合併到主表。我已經在我的博客上解釋了這項技術: http://www.jarloo.com/c-bulk-upsert-to-sql-server-tutorial/ 描述了該技術並顯示了代碼的操作方法。 – Kelly

回答

6

任何ORM工具的崩潰都在於它「喋喋不休」。大多數時候這是夠好的。有時不是。

簡短答案是「否」。

這就是爲什麼我有時會通過EF或NHibernate等選擇IDataReader。 對於批量插入操作,我將xml發送到存儲過程,然後將其碎片化並批量插入/更新或合併。

所以即使當我使用ORM時,我也會創建一個不依賴於EF(或NHibernate)的域庫......所以我有一個「安全閥」來在某些情況下通過ORM。

+0

謝謝,我的想法。感到驚訝的是,沒有任何OpenSource解決方案可以解決這個問題。我的項目一次寫入數千條記錄到db,並且每15秒發生一次,所以我想我會堅持使用普通的sql來更好地控制它。 – Kelly

+0

這是關鍵的事情。索引重新創建是減速。當您插入RBAR(通過激動行進行排序)時,必須在每次插入後重新創建索引。所以你需要做一些「基於集合」的事情,比如通過xml,粉碎它,然後做一個插入到dbo.MyTable中的東西。現在,你可以在插入暫存表的地方做一些巫術,然後發出「從暫存到現實」(以批量方式),但它不是非常EF'ish。但是,經驗法則(或其中一個主要法則)是「當我這樣做時,我的索引如何重建」。又名,被遺忘的物品 – granadaCoder

+0

我所做的大部分工作基本上都是一個upsert。 (更新或插入)我需要它們快速,並且表格中有數十億(幾萬億)行。我發現最快的方法是插入臨時表然後進行合併。我在這裏記錄它:http://www.jarloo.com/c-bulk-upsert-to-sql-server-tutorial/ – Kelly

0

恐怕EF不支持批量插入或更新。正如你所說的,EF目前會生成一堆Insert命令並分別執行它們(但都包含在單個事務中)。有一些計劃實施配料,不知道最近是否有進展。希望在EF6中,但我有點懷疑。

您可以在此discussion閱讀更多內容。

3

如果您的插入查詢是ANSI SQL,或者你不關心支持multipe數據庫與你的代碼,你仍然有借殼創建一個EF ADO.NET提供者,並執行一些原始的SQL調用

https://stackoverflow.com/a/1579220/98491

我會做這樣的事情

private void BulkInsert(IEnumerable<Person> Persons) 
{ 

    // use the information in the link to get your connection 
    DbConnection conn = ... 
    using (DbCommand cmd = conn.CreateCommand()) 
    { 

     var sb = new StringBuilder(); 
     sb.Append("INSERT INTO person (firstname, lastname) VALUES "); 
     var count = 0; 
     foreach(var person in persons) 
     { 
      if (count !=0) sb.Append(","); 
      sb.Append(GetInsertCommand(person, count++, cmd)); 
     } 

     if (count > 0) 
     { 
      cmd.CommandText = sb.ToString(); 
      cmd.ExecuteNonQuery(); 
     } 
    } 



    if (sb.Length > 0) 
     ExecuteNonQuery(sb.ToString()); 
} 

private string GetInsertCommand(Person person, int count, DbCommand cmd) 
{ 
    var firstname = "@firstname" + count.ToString(); 
    var lastname = "@lastname" + count.ToString(); 
    cmd.Parameters.Add(firstname, person.Firstname); 
    cmd.Parameters.Add(lastname, person.Firstname); 
    return String.Format("({0},{1})", firstname, lastname); 
} 

我必須承認,我沒有測試過,但是這應該是繞過EF一些批量插入一個快速和骯髒的方法,直到批量插入是核心的一部分。

更新

只是快速的想法。你是否嘗試過Migrations命名空間的...方法? 也許這個人做批量插入,沒有考慮它,但它是值得一試:

private void BatchInsert(IEnumerable<Person> persons) 
{ 
    context.Persons.AddOrUpdate(persons); 
} 

我知道這種方法可能會很慢,如果你定義一個類似於AddOrUpdate(p => p.Firstname, persons)鍵列,但我猜不specifing它,這應該是所有插入(不保證)

+0

沒有lambda參數的'AddOrUpdate'使用鍵屬性來查詢,所以它與AddOrUpdate(p => p.Id,persons)'基本相同。在插入之前,它仍然是集合中每個人的一個查詢(由關鍵字「SingleOrDefault」)。 – Slauma

+0

感謝您的更新,很高興知道。 –

3

沒有爲實體框架幾項改進oportunity:

集:

yourContext.Configuration.AutoDetectChangesEnabled = false; 
yourContext.Configuration.ValidateOnSaveEnabled = false; 

做100個插入包的SaveChanges() ...嘗試1000並查看更改。

由於在所有插入過程中,上下文都是相同的,所以您可以每過1000次插入就重建一次上下文對象。 var yourContext = new YourContext();

在我的導入數據過程中做了這個改進,花了7分鐘到6秒。

實際的數字...不可能是100的1000您的情況...嘗試它和啾啾它。

+0

我有一個相當複雜的遷移,我也使用UoW/Repo圖層,這爲我節省了大量的遷移時間。不知道我能夠使用EntityFramework.BulkInsert獲得和保存我的控制檯應用程序中的數據流。 – wintercyborg

2

可以使用bulk insert extension

用法:

using EntityFramework.BulkInsert.Extensions; 

context.BulkInsert(myEntities); 

用的DbContext:

using (var ctx = GetContext()) 
{ 
    using (var transactionScope = new TransactionScope()) 
    { 
     // some stuff in dbcontext  
     ctx.BulkInsert(entities);  
     ctx.SaveChanges(); 
     transactionScope.Complete(); 
    } 
} 
0

ASP .NET的核心與資源庫的版本快速的方法插入。

public virtual void AddRangeFastAndCommit(IEnumerable<T> entities) 
{ 
    MyDbContext localContext = new MyDbContext(_context.Options); 
    localContext.ChangeTracker.AutoDetectChangesEnabled = false; 

    foreach (var entity in entities) 
    { 
     localContext.Add(entity); 
    } 

    localContext.SaveChanges(); 
    localContext.Dispose(); 
}