2010-12-04 4 views
26

Ich muss ca. 30k Zeilen aus einer CSV-Datei in meine SQL-Datenbank importieren, dies dauert leider 20 Minuten.Wie beschleunige ich DbSet.Add()?

Fehlerbehebung mit einem Profiler zeigt mir DbSet.Add nimmt die meiste Zeit, aber warum?

Ich habe diese Entity Framework-Code-First-Klassen:

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

Für jedes Element in meinem for-Schleife tun I:

db.Articles.Add(article); 

Außerhalb der for-Schleife ich tun:

db.SaveChanges(); 

Es ist mit meinem lokalen SQLExpress-Server verbunden, aber ich denke, es gibt nichts geschrieben bis Sav eChanges wird aufgerufen, also ich denke, der Server wird nicht das Problem sein ....

+1

Hallo. Haben Sie Entity Framework losgeworden oder sqlbulkcopy zusammen mit EF verwendet? Ich bekomme das gleiche Problem mit. Add() –

+6

Wenn Sie diese setzen: 'db.Configuration.ValidateOnSaveEnabled = false; db.Configuration.AutoDetectChangesEnabled = false; ' Es gibt einen enormen Leistungszuwachs. Sie müssen sich Ihrer Werte sicher sein. –

+0

Verwenden Sie Backticks (') für Code in Kommentaren. Sieht interessant aus, ich werde später in diese Eigenschaften schauen ... –

Antwort

8

Jedes Element in einer Arbeitseinheit hat Overhead, wie es den Identity Manager überprüfen (und aktualisieren), zu verschiedenen hinzufügen muss Sammlungen, etc.

Das erste, was ich versuchen würde, ist in Gruppen von 500 zu mischen (ändern Sie diese Zahl zu passen), beginnend mit einem neuen (neuen) Objekt-Kontext jedes Mal - wie Sie sonst erwarten können Teleskopleistung. Wenn man sie in Chargen aufteilt, wird auch verhindert, dass eine megalithische Transaktion alles zum Stillstand bringt.

Darüber hinaus; SqlBulkCopy. Es ist für große Importe mit minimalem Overhead ausgelegt. Es ist jedoch nicht EF.

+0

+1 wenn in Ihrem Design anwendbar würde ich definitiv mit SqlBulkCopy gehen. –

+0

Ich versuche jetzt etwas damit zu erreichen, aber ich frage mich, ob es nur Übereinstimmungen basierend auf den Spaltennamen akzeptiert und nicht auf seinen Eigenschaften ... –

+2

Der Gruppenvorschlag machte es ein wenig schneller, aber es funktionierte nicht schnell genug. Nach einigen Iterationen durch böse Fehler habe ich SqlBulkCopy funktioniert, es ist aber böser Code, aber es funktioniert. Könnte es umgestalten oder überprüfen, ob es später Unterstützung für Masseneinfügung gibt ... Danke Marc und den Leuten im Chat, die einen ähnlichen Vorschlag gemacht haben! Und schau, etwas, das ** 20 Minuten dauerte ** dauert jetzt ** 2 Sekunden **, es ist Magie ... –

1

Ich habe das nicht wirklich versucht, aber meine Logik wäre, an ODBC-Treiber zu halten, um Datei in Datentabelle zu laden und dann sql gespeicherte Prozedur zu verwenden, um Tabelle an Prozedur zu übergeben.

Für den ersten Teil, versuchen: http://www.c-sharpcorner.com/UploadFile/mahesh/AccessTextDb12052005071306AM/AccessTextDb.aspx

Für den zweiten Teil dieses Verfahren für SQL versuchen: http://www.builderau.com.au/program/sqlserver/soa/Passing-table-valued-parameters-in-SQL-Server-2008/0,339028455,339282577,00.htm

Und schaffen SqlCommnand Objekt in C# und fügen Sie die zu seinem Parametern Sammlung SqlParameter ist SqlDbType. Strukturiert

Nun, ich hoffe, es hilft.

43

Per Kevin Ramen Kommentar (29. März) Ich kann das db.Configuration.AutoDetectChangesEnabled = false Einstellung bestätigen Geschwindigkeits

Lauf Add() auf 2324 Artikel standardmäßig lief 3min 15sec auf meiner Maschine, Deaktivieren der automatischen Erkennung führte einen großen Unterschied macht in der Operation abgeschlossen in 0,5 Sekunden.

http://blog.larud.net/archive/2011/07/12/bulk-load-items-to-a-ef-4-1-code-first-aspx

+0

Interessant ... :) –

+0

Dies ist eine tolle Sache zu wissen !! Es wurde ein großes Problem behoben, bei dem 4k-Datensätze mit EF eingefügt wurden, ohne dass ich meinen Code für die Verwendung von Bulk-Kopien neu erstellte. Ich denke, Bulk-Copy ist eine einfache Antwort, die die Leute tun, ohne das Problem weiter zu analysieren. In meinem Fall benötigte das sql-insert <1s und die EF-Erweiterung benötigte 30-40 Sekunden, sodass diese Problemumgehung perfekt funktioniert. Danke für die Information! – Alex

16

ich Kervin Ramen Kommentar hinzufügen werde sagen, dass, wenn Sie nur Einsätze tun werden (kein Updates oder löschen), dann können Sie in der Regel sicher die folgenden Eigenschaften festlegen, bevor irgendwelche Einsätze tun zum Kontext:

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

Ich hatte ein Problem mit einem einmaligen Massenimport bei meiner Arbeit. Ohne die obigen Eigenschaften festzulegen, dauerte das Hinzufügen von etwa 7500 komplizierten Objekten zum Kontext mehr als 30 Minuten.Wenn Sie die oben genannten Eigenschaften festlegen (also EF-Prüfungen deaktivieren und die Verfolgung ändern), wird der Import auf Sekunden reduziert.

Aber wieder, ich betone nur diese verwenden, wenn Sie Inserts tun. Wenn Sie Einfügungen mit Aktualisierungen/Löschungen mischen müssen, können Sie Ihren Code in zwei Pfade aufteilen und die EF-Prüfungen für den Einfügeteil deaktivieren und dann die Prüfungen für den Aktualisierungs-/Löschpfad wieder aktivieren. Ich habe diesen Ansatz erfolgreich verwendet, um das langsame DbSet.Add() Verhalten zu umgehen.

+0

Das ist sicherlich erstaunlich, und ich könnte das ausprobieren und es mit Masseneinsätzen vergleichen. Ich danke Ihnen für das Teilen! Auch danke für die Erinnerung, scheint ich vergaß diesen Kommentar, aber ich werde das morgen Abend sicher gehen ... –

+0

Nach dem Versuch scheint dies langsamer als Masseneinfügungen, so dass ich diesen Ansatz nicht verwenden kann. Im Detail mache ich 350.000 '.Add()' s (Entitäten ohne Referenzen auf andere Entitäten, nur Felder mit vernünftigen Werten), gefolgt von '.SaveChanges()', setze diese auf false, bevor Aufrufe Änderungen hinzufügen oder speichern und Zurück nach dem Speichern der Änderungen dauert viel länger als die Bulk-Einsätze, so dass ich nicht einmal die Mühe, es laufen zu lassen. –

+0

Ich kann es nicht glauben.Das hat meinen Tag gemacht und mein Chef wird glücklich sein. Funktioniert wie ein Charme :) – Alireza

4

Es ist ein extrem einfach und sehr schnell Erweiterung verwenden hier: https://efbulkinsert.codeplex.com/

It "Entity Framework Bulk Insert" genannt wird.

Erweiterung selbst ist im Namespace EntityFramework.BulkInsert.Extensions. So um die Erweiterung zu offenbaren Verfahren hinzufügen, indem

using EntityFramework.BulkInsert.Extensions; 

Und dann können Sie tun, um diese

context.BulkInsert(entities); 

BTW - Wenn Sie nicht mit dieser Erweiterung möchten aus irgendeinem Grund verwenden, können Sie auch statt Laufen versuchen db.Articles.Add (Artikel) für jeden Artikel, um jedes Mal eine Liste von mehreren Artikeln zu erstellen und dann AddRange (neu in EF Version 6 zusammen mit RemoveRange) zu verwenden, um sie zum dbcontext hinzuzufügen.

+0

Was macht es, um die Leistung zu verbessern? – PeterX

+0

Leider bekomme ich eine 'Der angegebene Schlüssel war nicht im Wörterbuch' Fehler und es scheint nicht eine gute Antwort über [hier] (http://stackoverflow.com/a/26427216/845584) – PeterX

+0

Es überspringt Die Validierung ruft jede Zeile auf und führt am Ende eine Validierung durch – ScottB

Verwandte Themen