當我使用我的xxxContext對象並向表中添加多個添加時,SaveChanges()實體框架如何將其解析爲SQL?它只是循環做插入到xxx或者如果有數百行,是否足夠聰明地發出大容量插入命令?.NET實體框架插入與批量插入
紅利問題:如果它沒有發出大容量插入有強制它的方式,所以我的數據庫性能不會被單獨的插入殺死?或者批量到臨時表然後像Upsert合併到原始表中?
當我使用我的xxxContext對象並向表中添加多個添加時,SaveChanges()實體框架如何將其解析爲SQL?它只是循環做插入到xxx或者如果有數百行,是否足夠聰明地發出大容量插入命令?.NET實體框架插入與批量插入
紅利問題:如果它沒有發出大容量插入有強制它的方式,所以我的數據庫性能不會被單獨的插入殺死?或者批量到臨時表然後像Upsert合併到原始表中?
任何ORM工具的崩潰都在於它「喋喋不休」。大多數時候這是夠好的。有時不是。
簡短答案是「否」。
這就是爲什麼我有時會通過EF或NHibernate等選擇IDataReader。 對於批量插入操作,我將xml發送到存儲過程,然後將其碎片化並批量插入/更新或合併。
所以即使當我使用ORM時,我也會創建一個不依賴於EF(或NHibernate)的域庫......所以我有一個「安全閥」來在某些情況下通過ORM。
謝謝,我的想法。感到驚訝的是,沒有任何OpenSource解決方案可以解決這個問題。我的項目一次寫入數千條記錄到db,並且每15秒發生一次,所以我想我會堅持使用普通的sql來更好地控制它。 – Kelly
這是關鍵的事情。索引重新創建是減速。當您插入RBAR(通過激動行進行排序)時,必須在每次插入後重新創建索引。所以你需要做一些「基於集合」的事情,比如通過xml,粉碎它,然後做一個插入到dbo.MyTable中的東西。現在,你可以在插入暫存表的地方做一些巫術,然後發出「從暫存到現實」(以批量方式),但它不是非常EF'ish。但是,經驗法則(或其中一個主要法則)是「當我這樣做時,我的索引如何重建」。又名,被遺忘的物品 – granadaCoder
我所做的大部分工作基本上都是一個upsert。 (更新或插入)我需要它們快速,並且表格中有數十億(幾萬億)行。我發現最快的方法是插入臨時表然後進行合併。我在這裏記錄它:http://www.jarloo.com/c-bulk-upsert-to-sql-server-tutorial/ – Kelly
恐怕EF不支持批量插入或更新。正如你所說的,EF目前會生成一堆Insert命令並分別執行它們(但都包含在單個事務中)。有一些計劃實施配料,不知道最近是否有進展。希望在EF6中,但我有點懷疑。
您可以在此discussion閱讀更多內容。
如果您的插入查詢是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它,這應該是所有插入(不保證)
沒有lambda參數的'AddOrUpdate'使用鍵屬性來查詢,所以它與AddOrUpdate(p => p.Id,persons)'基本相同。在插入之前,它仍然是集合中每個人的一個查詢(由關鍵字「SingleOrDefault」)。 – Slauma
感謝您的更新,很高興知道。 –
沒有爲實體框架幾項改進oportunity:
集:
yourContext.Configuration.AutoDetectChangesEnabled = false;
yourContext.Configuration.ValidateOnSaveEnabled = false;
做100個插入包的SaveChanges()
...嘗試1000並查看更改。
由於在所有插入過程中,上下文都是相同的,所以您可以每過1000次插入就重建一次上下文對象。 var yourContext = new YourContext();
在我的導入數據過程中做了這個改進,花了7分鐘到6秒。
實際的數字...不可能是100的1000您的情況...嘗試它和啾啾它。
我有一個相當複雜的遷移,我也使用UoW/Repo圖層,這爲我節省了大量的遷移時間。不知道我能夠使用EntityFramework.BulkInsert獲得和保存我的控制檯應用程序中的數據流。 – wintercyborg
用法:
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();
}
}
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();
}
使用的SqlBulkCopy的API – ErikEJ
可能重複[實體框架中插入的最快的方法(http://stackoverflow.com/questions/5940225/fastest-way-of-inserting-in-entity-framework) –
實體框架對此非常緩慢。找到了一種更快的方法。 基本上你會批量插入臨時表,然後從那裏發出一個合併到主表。我已經在我的博客上解釋了這項技術: http://www.jarloo.com/c-bulk-upsert-to-sql-server-tutorial/ 描述了該技術並顯示了代碼的操作方法。 – Kelly