Schnellste Methode zum Einfügen in Entity Framework

681

Ich suche nach dem schnellsten Weg zum Einfügen in Entity Framework.

Ich frage dies aufgrund des Szenarios, in dem Sie ein aktives TransactionScope haben und die Einfügung sehr groß ist (4000+). Dies kann möglicherweise länger als 10 Minuten dauern (Standardzeitlimit für Transaktionen). Dies führt zu einer unvollständigen Transaktion.

Bongo Sharp
quelle
1
Wie geht es dir aktuell?
Dustin Laine
Erstellen des TransactionScope, Instanziieren des DBContext, Öffnen der Verbindung und in einer for-each-Anweisung, die die Einfügungen und SavingChanges (für jeden Datensatz) ausführt, HINWEIS: TransactionScope und DBContext verwenden Anweisungen, und ich schließe die Verbindung endgültig Block
Bongo Sharp
Eine andere Antwort als Referenz: stackoverflow.com/questions/5798646/…
Ladislav Mrnka
2
Der schnellste Weg zum Einfügen in eine SQL-Datenbank ist nicht EF. AFAIK Sein BCP dann TVP + Merge / Insert.
StingyJack
1
Für diejenigen, die Kommentare lesen werden: Die zutreffendste, modernste Antwort ist hier.
Tanveer Badar

Antworten:

985

Zu Ihrer Bemerkung in den Kommentaren zu Ihrer Frage:

"... SavingChanges ( für jeden Datensatz ) ..."

Das ist das Schlimmste, was du tun kannst! Das Aufrufen SaveChanges()jedes Datensatzes verlangsamt Masseneinfügungen extrem. Ich würde ein paar einfache Tests durchführen, die sehr wahrscheinlich die Leistung verbessern werden:

  • SaveChanges()Einmal nach ALLEN Aufzeichnungen anrufen .
  • Rufen Sie SaveChanges()zum Beispiel nach 100 Datensätzen an.
  • Rufen Sie SaveChanges()zum Beispiel nach 100 Datensätzen auf, entsorgen Sie den Kontext und erstellen Sie einen neuen.
  • Deaktivieren Sie die Änderungserkennung

Für Masseneinsätze arbeite und experimentiere ich mit einem Muster wie diesem:

using (TransactionScope scope = new TransactionScope())
{
    MyDbContext context = null;
    try
    {
        context = new MyDbContext();
        context.Configuration.AutoDetectChangesEnabled = false;

        int count = 0;            
        foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
        {
            ++count;
            context = AddToContext(context, entityToInsert, count, 100, true);
        }

        context.SaveChanges();
    }
    finally
    {
        if (context != null)
            context.Dispose();
    }

    scope.Complete();
}

private MyDbContext AddToContext(MyDbContext context,
    Entity entity, int count, int commitCount, bool recreateContext)
{
    context.Set<Entity>().Add(entity);

    if (count % commitCount == 0)
    {
        context.SaveChanges();
        if (recreateContext)
        {
            context.Dispose();
            context = new MyDbContext();
            context.Configuration.AutoDetectChangesEnabled = false;
        }
    }

    return context;
}

Ich habe ein Testprogramm, das 560.000 Entitäten (9 Skalareigenschaften, keine Navigationseigenschaften) in die Datenbank einfügt. Mit diesem Code funktioniert es in weniger als 3 Minuten.

Für die Aufführung ist es wichtig, SaveChanges()nach "vielen" Datensätzen aufzurufen ("viele" um 100 oder 1000). Es verbessert auch die Leistung, den Kontext nach SaveChanges zu entsorgen und einen neuen zu erstellen. Dies löscht den Kontext von allen Entitäten, SaveChangestut dies nicht, die Entitäten sind weiterhin an den Kontext im Status gebunden Unchanged. Es ist die wachsende Größe der angehängten Entitäten im Kontext, die das Einfügen Schritt für Schritt verlangsamt. Es ist also hilfreich, es nach einiger Zeit zu löschen.

Hier sind einige Messungen für meine 560000 Entitäten:

  • commitCount = 1, recreateContext = false: viele Stunden (das ist Ihre aktuelle Prozedur)
  • commitCount = 100, recreateContext = false: mehr als 20 Minuten
  • commitCount = 1000, recreateContext = false: 242 Sek
  • commitCount = 10000, recreateContext = false: 202 Sek
  • commitCount = 100000, recreateContext = false: 199 Sek
  • commitCount = 1000000, recreateContext = false: Ausnahme wegen Speichermangel
  • commitCount = 1, recreateContext = true: mehr als 10 Minuten
  • commitCount = 10, recreateContext = true: 241 Sek
  • commitCount = 100, recreateContext = true: 164 Sek
  • commitCount = 1000, recreateContext = true: 191 Sek

Das Verhalten im ersten Test oben ist, dass die Leistung sehr nicht linear ist und mit der Zeit extrem abnimmt. ("Viele Stunden" ist eine Schätzung, ich habe diesen Test nie beendet, ich habe nach 20 Minuten bei 50.000 Entitäten angehalten.) Dieses nichtlineare Verhalten ist bei allen anderen Tests nicht so signifikant.

Slauma
quelle
89
@Bongo Sharp: Vergessen Sie nicht, AutoDetectChangesEnabled = false;den DbContext festzulegen. Es hat auch einen großen zusätzlichen Leistungseffekt: stackoverflow.com/questions/5943394/…
Slauma
6
Ja, das Problem ist, dass ich Entity Framework 4 verwende und AutoDetectChangesEnabled Teil von 4.1 ist. Trotzdem habe ich den Leistungstest durchgeführt und hatte ERSTAUNLICHE ERGEBNISSE. Es ging von 00:12:00 bis 00:00:22 SavinChanges auf jeder Entität war die olverload ... DANKE für Ihre answare! das ist, wonach ich gesucht habe
Bongo Sharp
10
Vielen Dank für den Kontext.Configuration.AutoDetectChangesEnabled = false; Tipp, es macht einen großen Unterschied.
Douglaz
1
@ dahacker89: Verwenden Sie die richtige Version EF> = 4.1 und DbContextNICHT ObjectContext?
Slauma
3
@ dahacker89: Ich schlage vor, dass Sie eine separate Frage für Ihr Problem mit vielleicht mehr Details erstellen. Ich kann hier nicht herausfinden, was los ist.
Slauma
176

Diese Kombination erhöht die Geschwindigkeit gut genug.

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
Arkhivania
quelle
46
Deaktivieren Sie ValidateOnSaveEnabled nicht blind. Möglicherweise sind Sie von diesem Verhalten abhängig und erkennen es erst, wenn es zu spät ist. Andererseits führen Sie möglicherweise eine Validierung an einer anderen Stelle im Code durch, und eine erneute Validierung von EF ist völlig unnötig.
Jeremy Cook
1
In meinem Test gingen 20.000 Zeilen von 101 Sekunden auf 88 Sekunden zurück. Nicht viel und was sind die Auswirkungen.
AH.
27
@JeremyCook Ich denke, Sie versuchen zu erreichen, dass diese Antwort viel besser wäre, wenn sie die möglichen Auswirkungen der Änderung dieser Eigenschaften von ihren Standardwerten (abgesehen von der Leistungsverbesserung) erklären würde. Genau.
Pseudocoder
1
Dies hat bei mir funktioniert, obwohl Sie, wenn Sie Datensätze im Kontext aktualisieren, DetectChanges () explizit aufrufen müssen
hillstuk
2
Diese können deaktiviert und dann mit einem Try-finally-Block wieder aktiviert werden
yellavon
98

Der schnellste Weg wäre die Verwendung der von mir entwickelten Bulk-Insert-Erweiterung

Hinweis: Dies ist ein kommerzielles Produkt, das nicht kostenlos ist

Es verwendet SqlBulkCopy und einen benutzerdefinierten Datenleser, um maximale Leistung zu erzielen. Infolgedessen ist es über 20-mal schneller als die Verwendung von normalem Einfügen oder AddRange EntityFramework.BulkInsert vs EF AddRange

Die Verwendung ist sehr einfach

context.BulkInsert(hugeAmountOfEntities);
maxlego
quelle
10
Schnell, aber nur die oberste Ebene einer Hierarchie.
CAD Kerl
65
Es ist nicht kostenlos.
Amir Saniyan
72
Anzeigen werden schlauer ... dies ist ein kostenpflichtiges Produkt und für Freiberufler sehr teuer. Sei gewarnt!
JulioQc
35
USD 600 für 1 Jahr Support und Upgrades? Du bist von Sinnen?
Camilo Terevinto
7
Ich bin
83

Sie sollten sich die Verwendung der System.Data.SqlClient.SqlBulkCopydafür ansehen . Hier ist die Dokumentation , und natürlich sind viele Tutorials online.

Entschuldigung, ich weiß, dass Sie nach einer einfachen Antwort gesucht haben, um EF dazu zu bringen, das zu tun, was Sie wollen, aber Massenoperationen sind nicht wirklich das, wofür ORMs gedacht sind.

Adam Rackis
quelle
1
Ich bin ein paar Mal auf die SqlBulkCopy gestoßen, als ich dies recherchierte, aber sie scheint sich eher an Table-to-Table-Beilagen zu orientieren. Leider hatte ich keine einfachen Lösungen erwartet, sondern Leistungstipps, wie zum Beispiel die Verwaltung des Status der Verbindung manuell, anstatt EF es für Sie tun zu lassen
Bongo Sharp
7
Ich habe SqlBulkCopy verwendet, um große Datenmengen direkt aus meiner Anwendung einzufügen. Sie haben grundsätzlich eine Datatable zu erstellen, füllen Sie es, dann passieren , dass zu BulkCopy. Es gibt ein paar Fallstricke beim Einrichten Ihrer Datentabelle (von denen ich die meisten leider vergessen habe), aber es sollte gut funktionieren
Adam Rackis
2
Ich habe den Proof of Concept durchgeführt, und wie versprochen funktioniert es sehr schnell, aber einer der Gründe, warum ich EF verwende, ist, dass das Einfügen relationaler Daten einfacher ist, z. B. wenn ich eine Entität einfüge, die bereits relationale Daten enthält Haben Sie sich jemals mit diesem Szenario befasst? Vielen Dank!
Bongo Sharp
2
Leider ist das Einfügen eines Netzes von Objekten in ein DBMS nicht wirklich etwas, was BulkCopy tun wird. Dies ist der Vorteil eines ORM wie EF. Die Kosten sind, dass es nicht skalierbar ist, um Hunderte ähnlicher Objektgraphen effizient zu erstellen.
Adam Rackis
2
SqlBulkCopy ist definitiv der richtige Weg, wenn Sie rohe Geschwindigkeit benötigen oder wenn Sie diesen Einsatz erneut ausführen. Ich habe schon mehrere Millionen Datensätze damit eingefügt und es ist extrem schnell. Das heißt, es sei denn, Sie müssen diesen Einsatz erneut ausführen, ist es möglicherweise einfacher, nur EF zu verwenden.
Neil
49

Ich stimme Adam Rackis zu. SqlBulkCopyist der schnellste Weg, um Massendatensätze von einer Datenquelle zu einer anderen zu übertragen. Ich habe damit 20.000 Datensätze kopiert und es dauerte weniger als 3 Sekunden. Schauen Sie sich das folgende Beispiel an.

public static void InsertIntoMembers(DataTable dataTable)
{           
    using (var connection = new SqlConnection(@"data source=;persist security info=True;user id=;password=;initial catalog=;MultipleActiveResultSets=True;App=EntityFramework"))
    {
        SqlTransaction transaction = null;
        connection.Open();
        try
        {
            transaction = connection.BeginTransaction();
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = "Members";
                sqlBulkCopy.ColumnMappings.Add("Firstname", "Firstname");
                sqlBulkCopy.ColumnMappings.Add("Lastname", "Lastname");
                sqlBulkCopy.ColumnMappings.Add("DOB", "DOB");
                sqlBulkCopy.ColumnMappings.Add("Gender", "Gender");
                sqlBulkCopy.ColumnMappings.Add("Email", "Email");

                sqlBulkCopy.ColumnMappings.Add("Address1", "Address1");
                sqlBulkCopy.ColumnMappings.Add("Address2", "Address2");
                sqlBulkCopy.ColumnMappings.Add("Address3", "Address3");
                sqlBulkCopy.ColumnMappings.Add("Address4", "Address4");
                sqlBulkCopy.ColumnMappings.Add("Postcode", "Postcode");

                sqlBulkCopy.ColumnMappings.Add("MobileNumber", "MobileNumber");
                sqlBulkCopy.ColumnMappings.Add("TelephoneNumber", "TelephoneNumber");

                sqlBulkCopy.ColumnMappings.Add("Deleted", "Deleted");

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
        }

    }
}
Irfons
quelle
1
Ich habe viele der in diesem Beitrag bereitgestellten Lösungen ausprobiert und SqlBulkCopy war bei weitem die schnellste. Pure EF dauerte 15 Minuten, aber mit einer Mischung aus Lösung und SqlBulkCopy konnte ich auf 1,5 Minuten kommen! Das war mit 2 Millionen Platten! Ohne DB-Indexoptimierung.
Jonas
Liste ist einfacher als DataTable. Es gibt eine AsDataReader()Erweiterungsmethode, die in dieser Antwort erklärt wird: stackoverflow.com/a/36817205/1507899
RJB
Aber es ist nur für Top-Entität nicht relational
Zahid Mustafa
1
@ ZahidMustafa: Ja. Es wird BulkInsert ausgeführt, nicht Bulk-Analyse-und-Relation-Tracing-On-Object-Graphs. Wenn Sie Relationen abdecken möchten, müssen Sie die Einfügereihenfolge analysieren und bestimmen und dann einzelne Ebenen in Bulk-Einfügungen einfügen und möglicherweise einige Schlüssel als aktualisieren benötigt, und Sie erhalten schnell eine maßgeschneiderte Lösung. Oder Sie können sich darauf verlassen, dass EF dies tut, keine Arbeit auf Ihrer Seite, sondern langsamer zur Laufzeit.
Quetzalcoatl
23

Ich würde diesen Artikel empfehlen, wie man Masseneinfügungen mit EF macht.

Entity Framework und langsame Massen-INSERTs

Er erkundet diese Bereiche und vergleicht die Leistung:

  1. Standard-EF (57 Minuten, um das Hinzufügen von 30.000 Datensätzen abzuschließen)
  2. Ersetzen durch ADO.NET-Code (25 Sekunden für dieselben 30.000)
  3. Context Bloat - Halten Sie das aktive Kontextdiagramm klein, indem Sie für jede Arbeitseinheit einen neuen Kontext verwenden (dieselben 30.000 Einfügungen dauern 33 Sekunden).
  4. Große Listen - Deaktivieren Sie AutoDetectChangesEnabled (verkürzt die Zeit auf ca. 20 Sekunden).
  5. Batching (bis zu 16 Sekunden)
  6. DbTable.AddRange () - (Leistung liegt im Bereich 12)
ShaTin
quelle
21

Da es hier nie erwähnt wurde, möchte ich EFCore.BulkExtensions hier weiterempfehlen

context.BulkInsert(entitiesList);                 context.BulkInsertAsync(entitiesList);
context.BulkUpdate(entitiesList);                 context.BulkUpdateAsync(entitiesList);
context.BulkDelete(entitiesList);                 context.BulkDeleteAsync(entitiesList);
context.BulkInsertOrUpdate(entitiesList);         context.BulkInsertOrUpdateAsync(entitiesList);         // Upsert
context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); // Sync
context.BulkRead(entitiesList);                   context.BulkReadAsync(entitiesList);
Manfred Wippel
quelle
1
Ich stimme diesem Vorschlag zu. Nachdem ich viele Homebrew-Lösungen ausprobiert hatte, reduzierte sich mein Einsatz von über 50 Sekunden auf 1 Sekunde. Und es ist MIT-Lizenz so einfach zu integrieren.
SouthShoreAK
ist dies für ef 6.x
Alok
Dies ist nur dann performanter als die Verwendung von AddRange, wenn es sich um mehr als 10 Entitäten handelt
Jackal
5
10 000 Einsätze gingen von 9 Minuten auf 12 Sekunden. Das verdient mehr Aufmerksamkeit!
Callisto
2
Wenn es eine Möglichkeit gibt, akzeptierte Antworten zu ändern, sollte dies jetzt die moderne akzeptierte Antwort sein. Und ich wünschte, das EF-Team hätte dies sofort zur Verfügung gestellt.
Tanveer Badar
18

Ich habe Slaumas Antwort untersucht (was großartig ist, danke für den Ideengeber) und die Chargengröße reduziert, bis ich die optimale Geschwindigkeit erreicht habe. Betrachten Sie die Ergebnisse des Slauma:

  • commitCount = 1, recreateContext = true: mehr als 10 Minuten
  • commitCount = 10, recreateContext = true: 241 Sek
  • commitCount = 100, recreateContext = true: 164 Sek
  • commitCount = 1000, recreateContext = true: 191 Sek

Es ist sichtbar, dass die Geschwindigkeit beim Bewegen von 1 auf 10 und von 10 auf 100 zunimmt, die Einfügegeschwindigkeit von 100 auf 1000 jedoch wieder abnimmt.

Ich habe mich also darauf konzentriert, was passiert, wenn Sie die Stapelgröße auf einen Wert zwischen 10 und 100 reduzieren. Hier sind meine Ergebnisse (ich verwende unterschiedliche Zeileninhalte, daher sind meine Zeiten von unterschiedlichem Wert):

Quantity    | Batch size    | Interval
1000    1   3
10000   1   34
100000  1   368

1000    5   1
10000   5   12
100000  5   133

1000    10  1
10000   10  11
100000  10  101

1000    20  1
10000   20  9
100000  20  92

1000    27  0
10000   27  9
100000  27  92

1000    30  0
10000   30  9
100000  30  92

1000    35  1
10000   35  9
100000  35  94

1000    50  1
10000   50  10
100000  50  106

1000    100 1
10000   100 14
100000  100 141

Basierend auf meinen Ergebnissen liegt das tatsächliche Optimum bei etwa 30 für die Chargengröße. Es ist weniger als 10 und 100. Das Problem ist, ich habe keine Ahnung, warum 30 optimal ist, noch hätte ich eine logische Erklärung dafür finden können.

Bewunderer Tuzović
quelle
2
Ich fand dasselbe bei Postrges und reinem SQL (es hängt von SQL ab, nicht von EF), dass 30 optimal ist.
Kamil Gareev
Ich habe die Erfahrung gemacht, dass sich das Optimum für unterschiedliche Verbindungsgeschwindigkeiten und Zeilengrößen unterscheidet. Für eine schnelle Verbindung und kleine Reihen kann das Optimum sogar> 200 Reihen sein.
Jing
18

Wie andere bereits gesagt haben, ist SqlBulkCopy der richtige Weg, wenn Sie eine wirklich gute Insert-Leistung wünschen.

Die Implementierung ist etwas umständlich, aber es gibt Bibliotheken, die Ihnen dabei helfen können. Es gibt einige, aber ich werde diesmal schamlos meine eigene Bibliothek anschließen: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

Der einzige Code, den Sie benötigen würden, ist:

 using (var db = new YourDbContext())
 {
     EFBatchOperation.For(db, db.BlogPosts).InsertAll(list);
 }

Wie viel schneller ist es? Sehr schwer zu sagen, da dies von so vielen Faktoren, der Computerleistung, dem Netzwerk, der Objektgröße usw. abhängt. Die von mir durchgeführten Leistungstests legen nahe, dass 25.000 Entitäten standardmäßig bei etwa 10 Sekunden eingefügt werden können bei localhost Sie Ihre EF-Konfiguration wie optimieren in den anderen Antworten erwähnt. Mit EFUtilities dauert das ungefähr 300ms. Noch interessanter ist, dass ich mit dieser Methode in weniger als 15 Sekunden ungefähr 3 Millionen Entitäten gespeichert habe, was einem Durchschnitt von ungefähr 200.000 Entitäten pro Sekunde entspricht.

Das einzige Problem ist natürlich, wenn Sie relevante Daten einfügen müssen. Dies kann mithilfe der oben beschriebenen Methode effizient in SQL Server durchgeführt werden. Sie benötigen jedoch eine ID-Generierungsstrategie, mit der Sie IDs im App-Code für das übergeordnete Element generieren können, damit Sie die Fremdschlüssel festlegen können. Dies kann mithilfe von GUIDs oder einer HiLo-ID-Generierung erfolgen.

Mikael Eliasson
quelle
Funktioniert gut. Die Syntax ist allerdings etwas ausführlich. Ich denke, es wäre besser, wenn Sie EFBatchOperationeinen Konstruktor hätten, den Sie an übergeben DbContext, anstatt an jede statische Methode zu übergeben. Generische Versionen von InsertAllund, UpdateAlldie die Sammlung automatisch finden, ähnlich wie DbContext.Set<T>, wären auch gut.
Kjbartel
Nur ein kurzer Kommentar, um sich zu bedanken! Mit diesem Code konnte ich 170.000 Datensätze in 1,5 Sekunden speichern! Bläst jede andere Methode, die ich ausprobiert habe, komplett aus dem Wasser.
Tom Glenn
@Mikael Ein Problem betrifft Identitätsfelder. Haben Sie bereits eine Möglichkeit, die Identitätseinfügung zu aktivieren?
Joe Phillips
1
Im Gegensatz zu EntityFramework.BulkInsert blieb diese Bibliothek frei. +1
Rudey
14

Dispose()Kontext schafft Probleme, wenn die Entitäten, Add()auf die Sie sich verlassen, andere vorinstallierte Entitäten (z. B. Navigationseigenschaften) im Kontext sind

Ich verwende ein ähnliches Konzept, um meinen Kontext klein zu halten und die gleiche Leistung zu erzielen

Aber anstatt Dispose()den Kontext zu erstellen und neu zu erstellen, entferne ich einfach die Entitäten, die bereits vorhanden sindSaveChanges()

public void AddAndSave<TEntity>(List<TEntity> entities) where TEntity : class {

const int CommitCount = 1000; //set your own best performance number here
int currentCount = 0;

while (currentCount < entities.Count())
{
    //make sure it don't commit more than the entities you have
    int commitCount = CommitCount;
    if ((entities.Count - currentCount) < commitCount)
        commitCount = entities.Count - currentCount;

    //e.g. Add entities [ i = 0 to 999, 1000 to 1999, ... , n to n+999... ] to conext
    for (int i = currentCount; i < (currentCount + commitCount); i++)        
        _context.Entry(entities[i]).State = System.Data.EntityState.Added;
        //same as calling _context.Set<TEntity>().Add(entities[i]);       

    //commit entities[n to n+999] to database
    _context.SaveChanges();

    //detach all entities in the context that committed to database
    //so it won't overload the context
    for (int i = currentCount; i < (currentCount + commitCount); i++)
        _context.Entry(entities[i]).State = System.Data.EntityState.Detached;

    currentCount += commitCount;
} }

Wickeln Sie es mit try catch ein und zeigen TrasactionScope()Sie sie bei Bedarf nicht hier an, um den Code sauber zu halten

Stephen Ho
quelle
1
Dies verlangsamte das Einfügen (AddRange) mit Entity Framework 6.0. Das Einfügen von 20.000 Zeilen stieg von ca. 101 Sekunden auf 118 Sekunden.
AH.
1
@ Stephen Ho: Ich versuche auch zu vermeiden, meinen Kontext zu veräußern. Ich kann verstehen, dass dies langsamer ist als das Neuerstellen des Kontexts, aber ich möchte wissen, ob Sie dies schneller genug gefunden haben, als den Kontext nicht neu zu erstellen, sondern mit einem commitCount-Satz.
Lerner
@Learner: Ich denke, es war schneller als den Kontext neu zu erstellen. Aber ich erinnere mich jetzt nicht wirklich, weil ich endlich auf SqlBulkCopy umgestiegen bin.
Stephen Ho
Letztendlich musste ich diese Technik verwenden, weil aus irgendeinem seltsamen Grund beim zweiten Durchlauf durch die while-Schleife noch etwas übrig blieb, obwohl ich alles in eine using-Anweisung eingeschlossen und im DbContext sogar Dispose () aufgerufen hatte . Wenn ich dem Kontext hinzufügen würde (beim 2. Durchgang), würde die Anzahl der Kontextsätze auf 6 anstatt nur auf einen springen. Die anderen Elemente, die willkürlich hinzugefügt wurden, wurden bereits im ersten Durchgang durch die while-Schleife eingefügt, sodass der Aufruf von SaveChanges beim zweiten Durchgang fehlschlug (aus offensichtlichen Gründen).
Hallmanac
9

Ich weiß, dass dies eine sehr alte Frage ist, aber einer hier sagte, er habe eine Erweiterungsmethode für die Verwendung von Masseneinfügungen mit EF entwickelt, und als ich nachschaute, stellte ich fest, dass die Bibliothek heute 599 US-Dollar kostet (für einen Entwickler). Vielleicht macht es Sinn für die gesamte Bibliothek, aber nur für die Masseneinfügung ist dies zu viel.

Hier ist eine sehr einfache Erweiterungsmethode, die ich gemacht habe. Ich benutze das zuerst beim Pairing mit der Datenbank (nicht zuerst mit Code getestet, aber ich denke, das funktioniert genauso). Ändern Sie YourEntitiesmit dem Namen Ihres Kontexts:

public partial class YourEntities : DbContext
{
    public async Task BulkInsertAllAsync<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            await conn.OpenAsync();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            await bulkCopy.WriteToServerAsync(table);
        }
    }

    public void BulkInsertAll<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            conn.Open();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            bulkCopy.WriteToServer(table);
        }
    }

    public string GetTableName(Type type)
    {
        var metadata = ((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace;
        var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

        var entityType = metadata
                .GetItems<EntityType>(DataSpace.OSpace)
                .Single(e => objectItemCollection.GetClrType(e) == type);

        var entitySet = metadata
            .GetItems<EntityContainer>(DataSpace.CSpace)
            .Single()
            .EntitySets
            .Single(s => s.ElementType.Name == entityType.Name);

        var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                .Single()
                .EntitySetMappings
                .Single(s => s.EntitySet == entitySet);

        var table = mapping
            .EntityTypeMappings.Single()
            .Fragments.Single()
            .StoreEntitySet;

        return (string)table.MetadataProperties["Table"].Value ?? table.Name;
    }
}

Sie können dies für jede Sammlung verwenden, von der Folgendes geerbt IEnumerablewird:

await context.BulkInsertAllAsync(items);
Guilherme
quelle
Bitte vervollständigen Sie Ihren Beispielcode. Wo ist BulkCopy
Seabizkit
1
Es ist schon da:await bulkCopy.WriteToServerAsync(table);
Guilherme
Vielleicht war mir nicht klar, dass Sie in Ihrem Artikel vorgeschlagen haben, eine Erweiterung zu erstellen ... was bedeutet, dass keine 3. Teilbibliothek benötigt wurde, obwohl in beiden Methoden tatsächlich SqlBulkCopy lib verwendet wurde. Dies hängt vollständig von SqlBulkCopy ab. Als ich fragte, woher BulkCopy kommt, handelt es sich um eine Erweiterungsbibliothek, über die Sie eine Erweiterungsbibliothek geschrieben haben. Wäre es nur sinnvoller zu sagen, hier ist, wie ich SqlBulkCopy lib verwendet habe.
Seabizkit
sollte conn.OpenAsync in der asynchronen Version verwenden
Robert
6

Versuchen Sie, eine gespeicherte Prozedur zu verwenden , die eine XML-Datei der Daten abruft, die Sie einfügen möchten.

Maxime
quelle
9
Die Übergabe von Daten als XML ist nicht erforderlich, wenn Sie sie nicht als XML speichern möchten. In SQL 2008 können Sie Parameter mit Tabellenwerten verwenden.
Ladislav Mrnka
Ich habe dies nicht geklärt, aber ich muss auch SQL 2005
Bongo Sharp
4

Ich habe eine generische Erweiterung des obigen Beispiels von @Slauma vorgenommen.

public static class DataExtensions
{
    public static DbContext AddToContext<T>(this DbContext context, object entity, int count, int commitCount, bool recreateContext, Func<DbContext> contextCreator)
    {
        context.Set(typeof(T)).Add((T)entity);

        if (count % commitCount == 0)
        {
            context.SaveChanges();
            if (recreateContext)
            {
                context.Dispose();
                context = contextCreator.Invoke();
                context.Configuration.AutoDetectChangesEnabled = false;
            }
        }
        return context;
    }
}

Verwendungszweck:

public void AddEntities(List<YourEntity> entities)
{
    using (var transactionScope = new TransactionScope())
    {
        DbContext context = new YourContext();
        int count = 0;
        foreach (var entity in entities)
        {
            ++count;
            context = context.AddToContext<TenancyNote>(entity, count, 100, true,
                () => new YourContext());
        }
        context.SaveChanges();
        transactionScope.Complete();
    }
}
Sgedda
quelle
4

Ich suche nach dem schnellsten Weg zum Einfügen in Entity Framework

Es sind einige Bibliotheken von Drittanbietern verfügbar, die Bulk Insert unterstützen:

  • Z.EntityFramework.Extensions ( empfohlen )
  • EFUtilities
  • EntityFramework.BulkInsert

Siehe: Entity Framework Bulk Insert-Bibliothek

Seien Sie vorsichtig, wenn Sie eine Bulk-Insert-Bibliothek auswählen. Nur Entity Framework-Erweiterungen unterstützen alle Arten von Zuordnungen und Vererbungen und es ist die einzige, die noch unterstützt wird.


Haftungsausschluss : Ich bin der Eigentümer von Entity Framework Extensions

Mit dieser Bibliothek können Sie alle Massenoperationen ausführen, die Sie für Ihre Szenarien benötigen:

  • Bulk SaveChanges
  • Masseneinsatz
  • Massenlöschung
  • Bulk-Update
  • Massenzusammenführung

Beispiel

// Easy to use
context.BulkSaveChanges();

// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);

// Perform Bulk Operations
context.BulkDelete(customers);
context.BulkInsert(customers);
context.BulkUpdate(customers);

// Customize Primary Key
context.BulkMerge(customers, operation => {
   operation.ColumnPrimaryKeyExpression = 
        customer => customer.Code;
});
Jonathan Magnan
quelle
19
Dies ist eine großartige Erweiterung, aber nicht kostenlos .
Okan Kocyigit
2
Diese Antwort ist ziemlich gut und EntityFramework.BulkInsert führt eine Masseneinfügung von 15 KB Zeilen in 1,5 Sekunden durch und funktioniert ziemlich gut für einen internen Prozess wie einen Windows-Dienst.
Pastor Cortes
4
Ja, 600 $ für Masseneinsatz. Total wert.
Eocron
1
@eocron Ja, es lohnt sich, wenn Sie es kommerziell verwenden. Ich sehe kein Problem mit 600 Dollar für etwas, das ich nicht stundenlang selbst bauen muss, was mich viel mehr als 600 Dollar kosten wird. Ja, es kostet Geld, aber wenn man meinen Stundensatz betrachtet, ist es gut angelegtes Geld!
Jordy van Eijk
3

Verwendung SqlBulkCopy:

void BulkInsert(GpsReceiverTrack[] gpsReceiverTracks)
{
    if (gpsReceiverTracks == null)
    {
        throw new ArgumentNullException(nameof(gpsReceiverTracks));
    }

    DataTable dataTable = new DataTable("GpsReceiverTracks");
    dataTable.Columns.Add("ID", typeof(int));
    dataTable.Columns.Add("DownloadedTrackID", typeof(int));
    dataTable.Columns.Add("Time", typeof(TimeSpan));
    dataTable.Columns.Add("Latitude", typeof(double));
    dataTable.Columns.Add("Longitude", typeof(double));
    dataTable.Columns.Add("Altitude", typeof(double));

    for (int i = 0; i < gpsReceiverTracks.Length; i++)
    {
        dataTable.Rows.Add
        (
            new object[]
            {
                    gpsReceiverTracks[i].ID,
                    gpsReceiverTracks[i].DownloadedTrackID,
                    gpsReceiverTracks[i].Time,
                    gpsReceiverTracks[i].Latitude,
                    gpsReceiverTracks[i].Longitude,
                    gpsReceiverTracks[i].Altitude
            }
        );
    }

    string connectionString = (new TeamTrackerEntities()).Database.Connection.ConnectionString;
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var transaction = connection.BeginTransaction())
        {
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = dataTable.TableName;
                foreach (DataColumn column in dataTable.Columns)
                {
                    sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
    }

    return;
}
Amir Saniyan
quelle
3

Als eine der schnellsten Möglichkeiten zum Speichern einer Liste müssen Sie den folgenden Code anwenden

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

AutoDetectChangesEnabled = false

Add, AddRange & SaveChanges: Erkennt keine Änderungen.

ValidateOnSaveEnabled = false;

Ändert den Tracker nicht

Sie müssen Nuget hinzufügen

Install-Package Z.EntityFramework.Extensions

Jetzt können Sie den folgenden Code verwenden

var context = new MyContext();

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

context.BulkInsert(list);
context.BulkSaveChanges();
Reza Jenabi
quelle
Kann ich Ihren Beispielcode für Massenaktualisierungen verwenden?
AminGolmahalle
4
Z Bibliothek ist nicht frei
SHADOW.NET
3

SqlBulkCopy ist super schnell

Dies ist meine Implementierung:

// at some point in my calling code, I will call:
var myDataTable = CreateMyDataTable();
myDataTable.Rows.Add(Guid.NewGuid,tableHeaderId,theName,theValue); // e.g. - need this call for each row to insert

var efConnectionString = ConfigurationManager.ConnectionStrings["MyWebConfigEfConnection"].ConnectionString;
var efConnectionStringBuilder = new EntityConnectionStringBuilder(efConnectionString);
var connectionString = efConnectionStringBuilder.ProviderConnectionString;
BulkInsert(connectionString, myDataTable);

private DataTable CreateMyDataTable()
{
    var myDataTable = new DataTable { TableName = "MyTable"};
// this table has an identity column - don't need to specify that
    myDataTable.Columns.Add("MyTableRecordGuid", typeof(Guid));
    myDataTable.Columns.Add("MyTableHeaderId", typeof(int));
    myDataTable.Columns.Add("ColumnName", typeof(string));
    myDataTable.Columns.Add("ColumnValue", typeof(string));
    return myDataTable;
}

private void BulkInsert(string connectionString, DataTable dataTable)
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        SqlTransaction transaction = null;
        try
        {
            transaction = connection.BeginTransaction();

            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = dataTable.TableName;
                foreach (DataColumn column in dataTable.Columns) {
                    sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction?.Rollback();
            throw;
        }
    }
}
Philip Johnson
quelle
3

[Update 2019] EF Core 3.1

Nach dem oben Gesagten funktionierte das Deaktivieren von AutoDetectChangesEnabled in EF Core einwandfrei: Die Einfügezeit wurde durch 100 geteilt (von vielen Minuten bis zu einigen Sekunden, 10.000 Datensätze mit tabellenübergreifenden Beziehungen).

Der aktualisierte Code lautet:

  context.ChangeTracker.AutoDetectChangesEnabled = false;
            foreach (IRecord record in records) {
               //Add records to your database        
            }
            context.ChangeTracker.DetectChanges();
            context.SaveChanges();
            context.ChangeTracker.AutoDetectChangesEnabled = true; //do not forget to re-enable
XavierAM
quelle
2

Hier ist ein Leistungsvergleich zwischen der Verwendung von Entity Framework und der Verwendung der SqlBulkCopy-Klasse anhand eines realistischen Beispiels: So fügen Sie komplexe Objekte in großen Mengen in die SQL Server-Datenbank ein

Wie andere bereits betont haben, sind ORMs nicht für den Massenbetrieb gedacht. Sie bieten Flexibilität, Trennung von Bedenken und andere Vorteile, aber Massenoperationen (außer Massenlesen) gehören nicht dazu.

Zoran Horvat
quelle
2

Eine weitere Option ist die Verwendung von SqlBulkTools, die bei Nuget erhältlich sind. Es ist sehr einfach zu bedienen und verfügt über einige leistungsstarke Funktionen.

Beispiel:

var bulk = new BulkOperations();
var books = GetBooks();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager
    .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
    {
        bulk.Setup<Book>()
            .ForCollection(books)
            .WithTable("Books") 
            .AddAllColumns()
            .BulkInsert()
            .Commit(conn);
    }

    trans.Complete();
}

Weitere Beispiele und erweiterte Verwendung finden Sie in der Dokumentation . Haftungsausschluss: Ich bin der Autor dieser Bibliothek und alle Ansichten sind meiner eigenen Meinung.

Greg R Taylor
quelle
2
Dieses Projekt wurde sowohl von NuGet als auch von GitHub gelöscht.
0xced
1

Nach meinem Wissen gibt es no BulkInsertinEntityFramework der Leistung der großen Einsätze zu erhöhen.

In diesem Szenario können Sie gehen mit SqlBulkCopy in ADO.netIhr Problem zu lösen

anishMarokey
quelle
Ich habe mir diese Klasse angesehen, aber sie scheint eher auf Einfügungen von Tabelle zu Tabelle ausgerichtet zu sein, nicht wahr?
Bongo Sharp
Nicht sicher, was du meinst, es hat eine Überlastung WriteToServer, die eine dauert DataTable.
Blindy
Nein, Sie können auch von .NET-Objekten in SQL einfügen. Was suchen Sie?
AnishMarokey
Eine Möglichkeit, potenziell Tausende von Datensätzen in die Datenbank innerhalb eines TransactionScope-Blocks einzufügen
Bongo Sharp
Sie können .Net TransactionScope technet.microsoft.com/en-us/library/bb896149.aspx
anishMarokey
1

Haben Sie jemals versucht, über einen Hintergrundarbeiter oder eine Hintergrundaufgabe einzufügen?

In meinem Fall füge ich 7760 Register ein, die in 182 verschiedenen Tabellen mit Fremdschlüsselbeziehungen (von NavigationProperties) verteilt sind.

Ohne die Aufgabe dauerte es zweieinhalb Minuten. Innerhalb einer Aufgabe ( Task.Factory.StartNew(...)) dauerte es 15 Sekunden.

Ich mache das erst, SaveChanges()nachdem ich alle Entitäten zum Kontext hinzugefügt habe. (um die Datenintegrität sicherzustellen)

Rafael AMS
quelle
2
Ich bin mir ziemlich sicher, dass der Kontext nicht threadsicher ist. Haben Sie Tests, um sicherzustellen, dass alle Entitäten gespeichert wurden?
Danny Varod
Ich weiß, dass das gesamte Entity-Framework überhaupt nicht threadsicher ist, aber ich füge nur die Objekte zum Kontext hinzu und speichere am Ende ... Es funktioniert hier perfekt.
Rafael AMS
Sie rufen also DbContext.SaveChanges () im Hauptthread auf, aber das Hinzufügen von Entitäten zum Kontext wird im Hintergrundthread ausgeführt, oder?
Prokurors
1
Ja, fügen Sie Daten innerhalb der Threads hinzu. Warten Sie, bis alle fertig sind. und Änderungen im Haupt-Thread speichern
Rafael AMS
Obwohl ich diesen Weg für gefährlich und fehleranfällig halte, finde ich ihn sehr interessant.
Lerner
1

Alle hier beschriebenen Lösungen helfen nicht weiter, denn wenn Sie SaveChanges () ausführen, werden Einfügeanweisungen einzeln an die Datenbank gesendet. So funktioniert Entity.

Wenn Ihre Reise zur Datenbank und zurück beispielsweise 50 ms beträgt, beträgt die zum Einfügen benötigte Zeit die Anzahl der Datensätze x 50 ms.

Sie müssen BulkInsert verwenden, hier ist der Link: https://efbulkinsert.codeplex.com/

Ich habe die Einfügezeit von 5-6 Minuten auf 10-12 Sekunden reduziert.

Aleksa
quelle
1

Sie können ein Massenpaket verwenden verwenden. Die Version Bulk Insert 1.0.0 wird in Projekten mit Entity Framework> = 6.0.0 verwendet.

Weitere Beschreibungen finden Sie hier - Quellcode für Massenoperationen

Nadeem
quelle
1

[NEUE LÖSUNG FÜR POSTGRESQL] Hey, ich weiß, dass es ein ziemlich alter Beitrag ist, aber ich bin kürzlich auf ein ähnliches Problem gestoßen, aber wir haben Postgresql verwendet. Ich wollte effektives Bulkinsert verwenden, was sich als ziemlich schwierig herausstellte. Ich habe in dieser Datenbank keine richtige freie Bibliothek dafür gefunden. Ich habe nur diesen Helfer gefunden: https://bytefish.de/blog/postgresql_bulk_insert/, der sich ebenfalls auf Nuget befindet. Ich habe einen kleinen Mapper geschrieben, der Eigenschaften wie Entity Framework automatisch zuordnet:

public static PostgreSQLCopyHelper<T> CreateHelper<T>(string schemaName, string tableName)
        {
            var helper = new PostgreSQLCopyHelper<T>("dbo", "\"" + tableName + "\"");
            var properties = typeof(T).GetProperties();
            foreach(var prop in properties)
            {
                var type = prop.PropertyType;
                if (Attribute.IsDefined(prop, typeof(KeyAttribute)) || Attribute.IsDefined(prop, typeof(ForeignKeyAttribute)))
                    continue;
                switch (type)
                {
                    case Type intType when intType == typeof(int) || intType == typeof(int?):
                        {
                            helper = helper.MapInteger("\"" + prop.Name + "\"",  x => (int?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type stringType when stringType == typeof(string):
                        {
                            helper = helper.MapText("\"" + prop.Name + "\"", x => (string)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type dateType when dateType == typeof(DateTime) || dateType == typeof(DateTime?):
                        {
                            helper = helper.MapTimeStamp("\"" + prop.Name + "\"", x => (DateTime?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type decimalType when decimalType == typeof(decimal) || decimalType == typeof(decimal?):
                        {
                            helper = helper.MapMoney("\"" + prop.Name + "\"", x => (decimal?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type doubleType when doubleType == typeof(double) || doubleType == typeof(double?):
                        {
                            helper = helper.MapDouble("\"" + prop.Name + "\"", x => (double?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type floatType when floatType == typeof(float) || floatType == typeof(float?):
                        {
                            helper = helper.MapReal("\"" + prop.Name + "\"", x => (float?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type guidType when guidType == typeof(Guid):
                        {
                            helper = helper.MapUUID("\"" + prop.Name + "\"", x => (Guid)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                }
            }
            return helper;
        }

Ich benutze es folgendermaßen (ich hatte eine Entität namens Undertaking):

var undertakingHelper = BulkMapper.CreateHelper<Model.Undertaking>("dbo", nameof(Model.Undertaking));
undertakingHelper.SaveAll(transaction.UnderlyingTransaction.Connection as Npgsql.NpgsqlConnection, undertakingsToAdd));

Ich habe ein Beispiel mit Transaktion gezeigt, aber es kann auch mit einer normalen Verbindung durchgeführt werden, die aus dem Kontext abgerufen wird. commitakingsToAdd enthält eine Auflistung normaler Entitätsdatensätze, die ich in die Datenbank einfügen möchte.

Diese Lösung, die ich nach ein paar Stunden Recherche und Ausprobieren habe, ist, wie Sie es erwarten können, viel schneller und endlich einfach zu bedienen und kostenlos! Ich rate Ihnen wirklich, diese Lösung zu verwenden, nicht nur aus den oben genannten Gründen, sondern auch, weil es die einzige ist, mit der ich keine Probleme mit Postgresql selbst hatte. Viele andere Lösungen funktionieren einwandfrei, zum Beispiel mit SqlServer.

Michał Pilarek
quelle
0

Das Geheimnis besteht darin, in eine identische leere Staging-Tabelle einzufügen. Einsätze blitzen schnell. Führen Sie dann eine einzelne Einfügung davon in Ihre große Haupttabelle aus. Schneiden Sie dann die Staging-Tabelle ab, die für den nächsten Stapel bereit ist.

dh.

insert into some_staging_table using Entity Framework.

-- Single insert into main table (this could be a tiny stored proc call)
insert into some_main_already_large_table (columns...)
   select (columns...) from some_staging_table
truncate table some_staging_table
Simon Hughes
quelle
Fügen Sie mit EF alle Ihre Datensätze zu einer leeren Staging-Tabelle hinzu. Verwenden Sie dann SQL, um in einer einzelnen SQL-Anweisung in die Haupttabelle (groß und langsam) einzufügen . Leeren Sie dann Ihren Staging-Tisch. Es ist eine sehr schnelle Möglichkeit, viele Daten in eine bereits große Tabelle einzufügen.
Simon Hughes
13
Wenn Sie sagen, dass Sie EF verwenden, fügen Sie die Datensätze zur Staging-Tabelle hinzu. Haben Sie dies tatsächlich mit EF versucht? Da EF mit jeder Einfügung einen separaten Aufruf an die Datenbank ausgibt, wird vermutlich derselbe Perf-Treffer angezeigt, den das OP zu vermeiden versucht. Wie vermeidet die Staging-Tabelle dieses Problem?
Jim Wooley
-1

Für mehr als (+4000) Einfügungen empfehle ich jedoch die Verwendung einer gespeicherten Prozedur. fügte die verstrichene Zeit hinzu. Ich habe es 11.788 Zeilen in 20 "eingefügt.Geben Sie hier die Bildbeschreibung ein

das ist es Code

 public void InsertDataBase(MyEntity entity)
    {
        repository.Database.ExecuteSqlCommand("sp_mystored " +
                "@param1, @param2"
                 new SqlParameter("@param1", entity.property1),
                 new SqlParameter("@param2", entity.property2));
    }
Marinpietri
quelle
-1

Verwenden Sie eine gespeicherte Prozedur, die Eingabedaten in Form von XML verwendet, um Daten einzufügen.

Fügen Sie aus Ihrem C # -Code-Pass Daten als XML ein.

zB in c # wäre die Syntax wie folgt:

object id_application = db.ExecuteScalar("procSaveApplication", xml)
Arun Tiwari
quelle
-7

Verwenden Sie diese Technik, um das Einfügen von Datensätzen in Entity Framework zu beschleunigen. Hier verwende ich eine einfache gespeicherte Prozedur, um die Datensätze einzufügen. Und um diese gespeicherte Prozedur auszuführen, verwende ich die .FromSql () -Methode von Entity Framework , die Raw SQL ausführt.

Der gespeicherte Prozedurcode:

CREATE PROCEDURE TestProc
@FirstParam VARCHAR(50),
@SecondParam VARCHAR(50)

AS
  Insert into SomeTable(Name, Address) values(@FirstParam, @SecondParam) 
GO

Als nächstes durchlaufen Sie alle Ihre 4000 Datensätze und fügen Sie den Entity Framework-Code hinzu, der den gespeicherten ausführt

Die Prozedur beginnt jede 100. Schleife.

Zu diesem Zweck erstelle ich eine Zeichenfolgenabfrage, um diese Prozedur auszuführen. Hänge weiterhin alle Datensätze an.

Überprüfen Sie dann, ob die Schleife in Vielfachen von 100 ausgeführt wird, und führen Sie sie in diesem Fall mit aus .FromSql().

Für 4000 Datensätze muss ich die Prozedur also nur 4000/100 = 40 Mal ausführen .

Überprüfen Sie den folgenden Code:

string execQuery = "";
var context = new MyContext();
for (int i = 0; i < 4000; i++)
{
    execQuery += "EXEC TestProc @FirstParam = 'First'" + i + "'', @SecondParam = 'Second'" + i + "''";

    if (i % 100 == 0)
    {
        context.Student.FromSql(execQuery);
        execQuery = "";
    }
}
Mallory H.
quelle
Dies ist möglicherweise effizient, entspricht jedoch der NICHT-Verwendung des Entity-Frameworks. Die OP-Frage war, wie die Effizienz im Kontext von Entity Framework maximiert werden kann
kall2sollies