2015-01-12 14 views
6

Ho quello che sembra essere un problema comune, ma non riesco a capire come ottenere il risultato desiderato. Ho un'entità annidata con le proprietà di navigazione definite su di esso come si vede nel seguente diagramma.Framework Entity, Inserimenti di massa e Mantenimento delle relazioni

enter image description here

La raccolta punti della mappa può potenzialmente essere abbastanza grande per un determinato MapLine e non ci può essere un numero piuttosto elevato di MapLines per un maplayer.

La domanda qui è qual è l'approccio migliore per ottenere un oggetto MapLayer inserito nel database utilizzando Entity Framework e mantenere comunque le relazioni definite dalle proprietà di navigazione?

Un'implementazione standard Entity Framework

dbContext.MapLayers.Add(mapLayer); 
dbContext.SaveChanges(); 

provoca un picco di memoria e tempi di ritorno piuttosto scarsa.

Ho provato l'attuazione del EntityFramework.BulkInsert packagebut it does not honor the relationships of the objects.

Questo sembra che sarebbe un problema che qualcuno ha eseguito in prima, ma non posso sembrano trovare tutte le risorse che spiegano come eseguire questa operazione.

Aggiornamento

ho cercato di attuare il suggerimento fornito da Richard, ma non sono la comprensione di come vorrei andare a questo per un soggetto nidificato come quello che ho descritto. Sto correndo partendo dal presupposto che ho bisogno di inserire l'oggetto MapLayer, poi MapLines, poi MapPoints per onorare la relazione PF/FK nel database. Attualmente sto provando il seguente codice ma questo non sembra essere corretto.

dbContext.MapLayers.Add(mapLayer); 
dbContext.SaveChanges(); 

List<MapLine> mapLines = new List<MapLine>(); 
List<MapPoint> mapPoints = new List<MapPoint>(); 
foreach (MapLine mapLine in mapLayer.MapLines) 
{ 
    //Update the mapPoints.MapLine properties to reflect the current line object 
    var updatedLines = mapLine.MapPoints.Select(x => { x.MapLine = mapLine; return x; }).ToList(); 

    mapLines.AddRange(updatedLines); 
} 

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

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

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

    scope.Complete(); 
} 

Aggiorna 2

Dopo aver provato diversi modi per raggiungere questo fine ho rinunciato e appena inserito il maplayer come entità e memorizzato il rapporto MapLines => MapPoints come stringa JSON crudo a un array di byte sull'entità MapLayer (poiché non sto interrogando su quelle strutture, questo funziona per me).

Come dice il proverbio "Non è bello, ma funziona".

Ho avuto un po 'di successo con il pacchetto BulkInsert e gestendo le relazioni al di fuori di EF, ma di nuovo si è imbattuto in un problema di memoria quando si tenta di utilizzare EF per reinserire i dati nel sistema. Sembra che attualmente EF non sia in grado di gestire in modo efficiente set di dati di grandi dimensioni e relazioni complesse.

+0

Puoi spiegare che cosa stai facendo con i dati dopo? Mi interessa, se non è meglio usare semplicemente un semplice formato binario per i tuoi dati. –

risposta

14

Ho avuto brutta esperienza con un grande contesto di salvataggio.Tutte quelle raccomandazioni sul salvataggio in iterazioni di 100 righe, di 1000 righe, quindi eliminazione del contesto o cancellazione di elenchi e distacchi di oggetti, assegnazione di null a tutto ecc. Ecc. - Sono tutte cazzate. Avevamo i requisiti per inserire quotidianamente milioni di righe in molte tabelle. Sicuramente non si dovrebbe usare l'entità in queste condizioni. Combatterai con perdite di memoria e diminuirai la velocità di inserimento quando procedono le iterazioni.

Il nostro primo miglioramento è stato creare stored procedure e aggiungerle al modello. È 100 volte più veloce di Context.SaveChanges() e non ci sono perdite, nessuna diminuzione della velocità nel tempo.

Ma non è stato sufficiente per noi e abbiamo deciso di utilizzare SqlBulkCopy. È super veloce 1000 volte più veloce quindi usando le stored procedure.

Quindi il mio suggerimento sarà: se hai molte righe da inserire ma il conteggio è sotto qualcosa come 50000 righe, usa stored procedure, importate nel modello; se hai centinaia di migliaia di righe, prova a provare SqlBulkCopy.

Ecco il codice:

EntityConnection ec = (EntityConnection)Context.Connection; 
SqlConnection sc = (SqlConnection)ec.StoreConnection; 

var copy = new SqlBulkCopy(sc, SqlBulkCopyOptions.CheckConstraints | SqlBulkCopyOptions.Default , null); 

copy.DestinationTableName = "TableName"; 
copy.ColumnMappings.Add("SourceColumn", "DBColumn"); 
copy.WriteToServer(dataTable); 
copy.Close(); 

Se si utilizza DbTransaction con il contesto, è possibile gestire per inserimento di massa utilizzando tale transazione pure, ma ha bisogno di qualche hack.

+0

"Ho avuto brutte esperienze con un enorme contesto di salvataggio: tutte quelle raccomandazioni sul salvataggio in iterazioni di 100 righe, di 1000 righe, quindi eliminazione del contesto o cancellazione di elenchi e distacchi di oggetti, assegnazione di null a tutto ecc. - leggi fino a questo punto e +1 già –

+0

ma per quanto riguarda il problema della relazione? come risolverlo? –

+1

@eranotzap, Se intendi il relashinship mentre l'inserimento bulk, abbiamo appena aggiunto 2 colonne aggiuntive alla tabella padre e riempiendolo nel codice. 1 è per porzione dire PortionID, altro è per relazione dire RelationID. Dopo l'inserimento collettivo selezioniamo i dati per porzione e selezioniamo ID e RelationID. Quindi ora ho relazioni e assegno ID appropriati ai record figlio confrontando per RelationID e assegnando ID da db. Poi faccio un altro bulkinsert per i bambini. –

6

Bulk Insert non è l'unico modo per aggiungere dati in modo efficiente utilizzando Entity Framework: un numero di alternative è dettagliato in this answer. Puoi utilizzare le ottimizzazioni suggerite qui (disabilitando il rilevamento delle modifiche), quindi puoi semplicemente aggiungere le cose normalmente.

Si noti che quando si aggiungono più elementi contemporaneamente, è necessario ricreare il proprio contesto abbastanza frequentemente per arrestare la perdita di memoria e il rallentamento che si otterrà.

Problemi correlati