2013-06-12 14 views
9

Kiedy używam mojego obiektu xxxContext i wydaje kilka Adds do tabeli, następnie SaveChanges() w jaki sposób framework encji rozwiązuje to w SQL? Czy po prostu zapętli robiąc wstawić do xxx lub jeśli istnieją setki wierszy, czy jest wystarczająco inteligentny, aby wydać polecenie wstawiania luzem?.NET Entity Framework Insert vs Bulk Insert

Pytanie dodatkowe: Jeśli nie ma problemu z włożeniem zbiorczym, czy istnieje sposób, aby go zmusić, aby moja wydajność bazy danych nie została zabita przez osobne wstawki? Lub na masowym stole tymczasowym, a następnie połączyć się z oryginalnym stołem jak Upsert?

+1

pomocą SqlBulkCopy API – ErikEJ

+0

możliwie duplikat [najszybszy sposób umieszczania w jednostce ram] (http://stackoverflow.com/questions/5940225/fastest-way-of-inserting-in-entity-framework) –

+0

Struktura Entity jest bardzo powolna. Znalazłem sposób, aby zrobić to znacznie szybciej. Zasadniczo wstawiasz luzem do tabeli tymczasowej, a następnie wywołuje scalanie stamtąd do głównej tabeli. Wyjaśniłem technikę na moim blogu tutaj: http://www.jarloo.com/c-bulk-upsert-to-sql-server-tutorial/ Opisuje technikę i pokazuje kod, jak to zrobić. – Kelly

Odpowiedz

6

Upadek jakiegokolwiek narzędzia ORM polega na tym, że jest "rozmowny". Najczęściej jest to wystarczająco dobre. Czasami tak nie jest.

Krótka odpowiedź to "nie".

Dlatego nadal czasami podnieść IDataReader nad EF lub NHibernate itp I masowych operacji wstawiania, wyślę xml do procedury przechowywanej, i zniszczyć go i luzem insert/update lub scalić stamtąd.

Więc nawet gdy używam ORM, tworzę Bibliotekę Domenową, która nie jest zależna od EF (lub NHibernate) ...... więc mam "zawór bezpieczeństwa", aby przejść przez ORM w pewnych sytuacjach.

+0

Dzięki, co myślałem. Zaskoczeni, że nie istnieje żadne rozwiązanie OpenSource, które zajmuje się tym. Mój projekt zapisuje tysiące rekordów na raz do db i dzieje się to co 15 sekund, więc myślę, że będę trzymać się zwykłego sql, gdzie mogę lepiej to kontrolować. – Kelly

+0

Oto kluczowa sprawa. Odtwarzaniem indeksu jest spowolnienie. Po wstawieniu RBAR (wiersz przez agonizing wiersz), musisz ponownie utworzyć indeks po każdym wstawieniu. Robisz więc coś "opartego na ustawieniach", jak przekazywanie xml, niszczenie go, a następnie robienie wstawiania do dbo.MyTable select (kilka rzędów) od czegoś. Teraz możesz zrobić trochę voodoo, gdzie wstawisz do stołu pomostowego, a następnie wydasz "przejście z inscenizacji na rzeczywistą" (w masywny sposób), ale to nie jest bardzo EF. Ale znowu, regułą (lub jednym z głównych) jest "w jaki sposób moje indeksy są przebudowywane, kiedy robię to w ten sposób". Aka, zapomniany przedmiot – granadaCoder

+0

Większość tego, co robię, jest w zasadzie uzasadniona. (zaktualizuj lub wstaw) Potrzebuję, aby były szybkie, a tablice, które wypuszczam, mają miliardy (prawie biliony) wierszy. Najszybsze podejście, jakie znalazłem, polega na wstawieniu do tabeli tymczasowej, a następnie wykonaniu scalenia. Udokumentowałem to tutaj: http://www.jarloo.com/c-bulk-upsert-to-sql-server-tutorial/ – Kelly

0

Obawiam się, że EF nie obsługuje wstawiania zbiorczego lub aktualizacji. Jak już powiedziałeś, EF wygeneruje kilka poleceń Insert i wykona je osobno (ale wszystkie zawinięte w jedną transakcję). Było kilka planów wdrożenia dozowania, nie ma pewności, czy w ostatnim czasie nastąpił jakiś postęp. Mam nadzieję, że w EF6, ale jakoś wątpię.

Możesz przeczytać więcej w tym discussion.

3

Jeśli zapytań INSERT są ANSI SQL lub nie dbają o wspieranie baz Nieograniczone równoczesne ze swoim kodzie, nadal masz backdoora do tworzenia dostawcy ADO.NET z EF i wykonać trochę surowego SQL wywołań

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

chciałbym zrobić coś takiego

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); 
} 

muszę przyznać, że jej nie testowane, ale powinno to być szybki i brudny sposób ominąć EF dla niektórych masowych wstawki aż Duże wkładki są częścią jądra .

Aktualizacja

Tylko szybki pomysł. Czy wypróbowałeś metodę ... z przestrzeni nazw Migrations? Może to się robi wkładki luzem, nie patrzeć na to, ale warto spróbować:

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

wiem, ta metoda może być powolna jeśli zdefiniować kolumnę klucza jak AddOrUpdate(p => p.Firstname, persons) ale przypuszczam bez specifing go , które powinny być wszystkie wkładki (nie gwarantowane)

+0

'AddOrUpdate' bez parametru lambda używa właściwości key do zapytanie, więc zasadniczo jest to identyczne z 'AddOrUpdate (p => p.Id, persons)'. Wciąż będzie to jedno zapytanie ("SingleOrDefault" wg klucza) na osobę w kolekcji, zanim zostanie wstawione. – Slauma

+0

Dzięki za aktualizację, dobrze wiedzieć. –

3

jest okazja do kilku ulepszeń w Entity Framework:

Set:

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

Wykonaj SaveChanges() w paczkach po 100 wkładek ... spróbuj z 1000 i zobacz zmiany.

Ponieważ podczas wszystkich tych wstawień kontekst jest taki sam, można przebudować obiekt kontekstowy co 1000 wstawek. var yourContext = new YourContext();

Wykonanie tych ulepszeń w procesie importowania danych z mojego, trwało od 7 minut do 6 sekund.

Rzeczywiste liczby ... nie mogą wynosić 100's o 1000 w twoim przypadku ... spróbuj i wyśpiewaj to.

+0

Mam dość złożoną migrację, w której również używam warstwy UoW/Repo, co pozwoliło mi zaoszczędzić znaczną część czasu migracji. Nie jestem pewien, czy mogę skorzystać z EntityFramework.BulkInsert podanego przepływu pozyskiwania i zapisywania danych w mojej aplikacji konsolowej. – wintercyborg

2

można użyć bulk insert extension

Zastosowanie:

using EntityFramework.BulkInsert.Extensions; 

context.BulkInsert(myEntities); 

z DbContext:

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

ASP .NET rdzenia wersja szybka metoda Wstaw z repozytorium.

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(); 
}