2015-08-26 27 views
9

Sto usando EF6 e, a causa della bassa velocità del metodo AddRange(), devo usare BulkInsert. Quindi ho aggiunto il pacchetto NuGet di BulkInsert per EF6 tramite here.Entity Framework Bulk Insert Throws KeyNotFoundException error

La prima cosa che ho ricevuto dopo aver aggiunto le dll s era questo avvertimento:

conflitti Trovato tra le diverse versioni dello stesso assembly dipendente. Impostare la proprietà "AutoGenerateBindingRedirects" su true nel file di progetto.

ho fatto un List di tutti i miei Contact entità cioè contactsToInsert che devono essere aggiunti (I miei contatti hanno una chiave esterna in un'altra tabella, anche). Quando ho provato a eseguire il seguente codice, ricevo un KeyNotFoundException che afferma "La chiave specificata non era presente nel dizionario".

using (var db = new Entities(myConnectionString)) 
{ 
    db.BulkInsert(contactsToInsert); 
    db.SaveChanges(); 
} 

NB. Sto eseguendo BulkInsert all'interno di BackgroundWorker. Potrebbe essere questa la causa del problema a giudicare dallo this fix?

StackTrace:

at System.Collections.Generic.Dictionary`2.get_Item(TKey key) 
    at EntityFramework.MappingAPI.Mappers.MapperBase.BindForeignKeys() in c:\dev\EntityFramework.MappingAPI\trunk\src\EntityFramework.MappingAPI\Mappers\MapperBase.cs:line 603 
    at EntityFramework.MappingAPI.Mappings.DbMapping..ctor(DbContext context) in c:\dev\EntityFramework.MappingAPI\trunk\src\EntityFramework.MappingAPI\Mappings\DbMapping.cs:line 101 
    at EntityFramework.MappingAPI.EfMap.Get(DbContext context) in c:\dev\EntityFramework.MappingAPI\trunk\src\EntityFramework.MappingAPI\EfMap.cs:line 60 
    at EntityFramework.MappingAPI.Extensions.MappingApiExtensions.Db(DbContext ctx, Type type) in c:\dev\EntityFramework.MappingAPI\trunk\src\EntityFramework.MappingAPI\Extensions\MappingApiExtensions.cs:line 51 
    at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer) 
    at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector) 
    at EntityFramework.BulkInsert.Helpers.MappedDataReader`1..ctor(IEnumerable`1 enumerable, IEfBulkInsertProvider provider) in c:\dev\EntityFramework.BulkInsert\dev\Src\EntityFramework.BulkInsert\Helpers\MappedDataReader.cs:line 58 
    at EntityFramework.BulkInsert.Providers.EfSqlBulkInsertProviderWithMappedDataReader.Run[T](IEnumerable`1 entities, SqlTransaction transaction, BulkInsertOptions options) in c:\dev\EntityFramework.BulkInsert\dev\Src\EntityFramework.BulkInsert\Providers\EfSqlBulkInsertProviderWithMappedDataReader.cs:line 22 
    at EntityFramework.BulkInsert.Providers.ProviderBase`2.Run[T](IEnumerable`1 entities, IDbTransaction transaction, BulkInsertOptions options) in c:\dev\EntityFramework.BulkInsert\dev\Src\EntityFramework.BulkInsert\Providers\ProviderBase.cs:line 77 
    at EntityFramework.BulkInsert.Providers.ProviderBase`2.Run[T](IEnumerable`1 entities, BulkInsertOptions options) in c:\dev\EntityFramework.BulkInsert\dev\Src\EntityFramework.BulkInsert\Providers\ProviderBase.cs:line 109 
    at EntityFramework.BulkInsert.Extensions.BulkInsertExtension.BulkInsert[T](DbContext context, IEnumerable`1 entities, SqlBulkCopyOptions sqlBulkCopyOptions, Nullable`1 batchSize) in c:\dev\EntityFramework.BulkInsert\dev\Src\EntityFramework.BulkInsert\Extensions\BulkInsertExtension.cs:line 95 
    at EntityFramework.BulkInsert.Extensions.BulkInsertExtension.BulkInsert[T](DbContext context, IEnumerable`1 entities, Nullable`1 batchSize) in c:\dev\EntityFramework.BulkInsert\dev\Src\EntityFramework.BulkInsert\Extensions\BulkInsertExtension.cs:line 75 
    at Prospect.Update.bw_DoWork(Object sender, DoWorkEventArgs e) in c:\Users\pedram.mobedi\Documents\Visual Studio 2013\Projects\Prospect\Update.cs:line 546 
    at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument) 
+1

ho incontrato la cosa esatta, e non sono stati in grado di trovare una correzione per questo. In definitiva, ho dovuto creare un secondo contesto EF senza proprietà di navigazione e solo la classe che sto cercando di inserire usando BulkInsert. Questa è solo una soluzione, e non è certamente una correzione, ma mi ha permesso di continuare a lavorare. –

+0

Hai controllato le versioni recenti che funzionano con 6.1.3. Ho usato le librerie per alcuni progetti ora, nessun problema finora :) Un altro punto: puoi fornire la definizione della classe Contact? –

risposta

0

Ok, avevo un stesso errore non potrebbe trovare alcuna risposta on-line e quindi abbiamo dovuto guardare in profondità: ecco che cosa ho pensato:

Quando il soggetto ha un ereditato e le entità figlio se non definite come parte del DBSet segnala un errore di questo tipo, in secondo luogo, non sono sicuro del perché si aspetta il nuovo DbContext con solo l'entità correlata utilizzata per bulkInsertions se ci sono altre entità là fuori che solleva lo stesso errore.

Quindi è la causa 2 quindi ho dovuto risolvere entrambi, e correre come un cavallo !!!

la sua pena di provare, in modo da dare una prova

-1

La libreria "BulkInsert" è molto veloce, ma non è molto flessibile e non supportato.

Non supporta tutti i tipi di ereditarietà (TPC, TPT) e presenta alcuni problemi con il mapping delle colonne.

Il problema riscontrato è per uno di questi motivi.

Diniego: Sono il titolare del progetto Entity Framework Extensions

Questa libreria è la libreria finale per le prestazioni e permettono di:

  • BulkSaveChanges
  • BulkInsert
  • BulkUpdate
  • BulkDelete
  • BulkMerge

Sono supportate tutte le ereditarietà e le associazioni.

Esempio:

using (var db = new Entities(myConnectionString) 
{ 
    db.BulkInsert(contactsToInsert); 
} 

// BulkSaveChanges is slower than BulkInsert but way faster then SaveChanges 
using (var db = new Entities(myConnectionString)) 
{ 
    db.Contacts.AddRange(contactsToInsert); 
    db.BulkSaveChanges(); 
} 
+3

Probabilmente dovresti menzionare che non è gratuito, nemmeno GPL. –

2

Con modifiche al codice in this blog post, ecco cosa ha funzionato per il mio setup Codice Prima perfetto API dopo aver incontrato lo stesso errore "The given key was not present in the dictionary" su BulkInsert(). L'unica dipendenza è il metodo di estensione ToDataTable() che si trova nello snippet DataExtensions del post di cui sopra.

La parte rilevante è il metodo GetColumnMappings() che ottiene nome preferito della struttura classe POCO (quello specificato nel codice) come nome colonna di origine (nel enumerable-girare-datatable) unendolo con l'elemento di metadati nome (il nome della colonna DB) come nome della colonna di destinazione.

GetColumnMappings():

private IEnumerable<SqlBulkCopyColumnMapping> GetColumnMappings<T>() 
{ 
    var storageMetadata = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace().GetItems(DataSpace.SSpace); 
    var entityPropMembers = storageMetadata 
     .Where(s => (s.BuiltInTypeKind == BuiltInTypeKind.EntityType)) 
     .Select(s => (EntityType)s) 
     .Where(p => p.Name == typeof(T).Name) 
     .Select(p => (IEnumerable<EdmMember>)(p.MetadataProperties["Members"].Value)) 
     .First(); 

    var sourceColumns = entityPropMembers.Select(m => (string)m.MetadataProperties["PreferredName"].Value); 
    var destinationColumns = entityPropMembers.Select(m => m.Name); 

    return Enumerable.Zip(sourceColumns, destinationColumns, (s, d) => new SqlBulkCopyColumnMapping(s, d)); 
} 

codice completo:

// Modified from: https://ruijarimba.wordpress.com/2012/03/25/bulk-insert-dot-net-applications-part1 and 
// https://ruijarimba.wordpress.com/2012/03/18/entity-framework-get-mapped-table-name-from-an-entity/ 

internal class BulkInserter 
{ 
    private readonly ObjectContext objectContext; 

    private readonly IDbConnection connection; 

    internal BulkInserter(DbContext contextAdapter) 
    { 
     objectContext = ((IObjectContextAdapter)contextAdapter).ObjectContext; 
     connection = contextAdapter.Database.Connection; 
    } 

    public void Insert<T>(IEnumerable<T> items) where T : class 
    { 
     EnsureOpenConnection(); 
     using (var bulkCopy = new SqlBulkCopy((SqlConnection)connection) 
     { 
      DestinationTableName = GetTableName<T>(), 
     }) 
     { 
      foreach (var mapping in GetColumnMappings<T>()) 
      { 
       bulkCopy.ColumnMappings.Add(mapping); 
      } 

      bulkCopy.WriteToServer(items.ToDataTable()); 
     } 
    } 
    private void EnsureOpenConnection() 
    { 
     if (connection.State == ConnectionState.Closed) 
     { 
      connection.Open(); 
     } 
    } 

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 
    private string GetTableName<T>() where T : class 
    { 
     string sql = objectContext.CreateObjectSet<T>().ToTraceString(); 
     Regex regex = new Regex("FROM (?<table>.*) AS"); 
     Match match = regex.Match(sql); 

     string table = match.Groups["table"].Value; 
     return table; 
    } 

    private IEnumerable<SqlBulkCopyColumnMapping> GetColumnMappings<T>() 
    { 
     var storageMetadata = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace().GetItems(DataSpace.SSpace); 
     var entityPropMembers = storageMetadata 
      .Where(s => (s.BuiltInTypeKind == BuiltInTypeKind.EntityType)) 
      .Select(s => (EntityType)s) 
      .Where(p => p.Name == typeof(T).Name) 
      .Select(p => (IEnumerable<EdmMember>)(p.MetadataProperties["Members"].Value)) 
      .First(); 

     var sourceColumns = entityPropMembers.Select(m => (string)m.MetadataProperties["PreferredName"].Value); 
     var destinationColumns = entityPropMembers.Select(m => m.Name); 

     return Enumerable.Zip(sourceColumns, destinationColumns, (s, d) => new SqlBulkCopyColumnMapping(s, d)); 
    } 
} 
+0

Ci scusiamo per il ritardo. Questo è stato pubblicato un anno fa. Controllerò e tornerò su questo appena sarò di nuovo a quel progetto. – Disasterkid