2015-08-26 20 views
9

Używam EF6 i ze względu na niską prędkość metody AddRange() potrzebuję użyć BulkInsert. Dodałem więc pakiet NuGet BulkInsert dla EF6 przez here.Entity Framework Bulk Insert zgłasza błąd KeyNotFoundException

Pierwszą rzeczą, jaką otrzymał po dodaniu dll s było to ostrzeżenie:

Znaleziono konflikty między różnymi wersjami tego samego zestawu zależnego . Ustaw właściwość "AutoGenerateBindingRedirects" na w pliku projektu.

Zrobiłem List wszystkich moich Contact podmiotów mianowicie contactsToInsert które muszą być dodane (Moje kontakty masz klucza obcego w innej tabeli, zbyt). Kiedy próbowałem uruchomić następujący kod, otrzymuję KeyNotFoundException, który twierdzi "Podany klucz nie był obecny w słowniku".

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

NB. Uruchomiłem BulkInsert wewnątrz BackgroundWorker. Czy to może być przyczyną problemu, sądząc po 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

mam napotkasz dokładnym rzeczy, i nie byli w stanie znaleźć rozwiązanie tego. Ostatecznie musiałem stworzyć drugi kontekst EF bez właściwości nawigacyjnych i tylko klasę, którą próbuję wstawić za pomocą BulkInsert. To tylko obejście i na pewno nie jest to poprawka, ale pozwoliło mi dalej pracować. –

+0

Czy sprawdziłeś najnowsze wersje współpracujące z 6.1.3. Korzystałem już z bibliotek dla niektórych projektów, żadnych problemów do tej pory :). Kolejna kwestia: czy możesz podać swoją definicję klasy kontaktów? –

Odpowiedz

0

Ok, miałem ten sam błąd nie mogliśmy znaleźć żadnych odpowiedzi online, więc musieli szukać głęboko więc o to, co pomyślałem:

Gdy jednostka dziedziczna i elementy podrzędne, jeśli nie są zdefiniowane jako część DBSet, oznaczają taki błąd, po drugie, nie są pewne, dlaczego spodziewa się nowego DbContext z tylko pokrewnym bytem użytym dla bulkInsertions, jeśli istnieją jakiekolwiek inne obiekty tam, to podnosi ten sam błąd.

Więc to jest ta 2 przyczyna, więc musiałem naprawić oba i biegać jak koń !!!

warto spróbować, więc Spróbuj

-1

W „BulkInsert” Biblioteka jest bardzo szybki, ale nie bardzo elastyczny i nieobsługiwane.

Nie obsługuje wszystkich rodzajów dziedziczenia (TPC, TPT) i ma pewne problemy z mapowaniem kolumn.

Ten problem dotyczy jednego z następujących powodów.

Zastrzeżone: Jestem właścicielem projektu Entity Framework Extensions

Biblioteka ta jest ostatecznym biblioteki pod kątem wydajności i pozwala na:

  • BulkSaveChanges
  • BulkInsert
  • BulkUpdate
  • BulkDelete
  • BulkMerge

Wszystkie dziedziczenie i asocjacja są obsługiwane.

Przykład:

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

Powinieneś chyba wspomnieć, że nie jest darmowy, nawet GPL. –

2

Z modyfikacje kodu w this blog post, oto co pracował dla mojej konfiguracji Code First Fluent API po napotkaniu tego samego "The given key was not present in the dictionary" błąd na BulkInsert(). Jedyną zależnością jest tutaj metoda rozszerzenia ToDataTable() znajdująca się w kodzie DataExtensions wspomnianego wpisu.

Właściwa część jest metoda GetColumnMappings() który dostaje klasie m.in. poco preferowaną nazwę (ten podany w kodzie) jako nazwa kolumny źródła (w przeliczalny toczone-DataTable) i pary go z członkiem metadane na nazwa (nazwa kolumny DB) jako nazwa kolumny docelowej.

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

pełny kod:

// 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

Przepraszamy za opóźnienie. Zostało to opublikowane rok temu. Sprawdzę i wrócę do tego, gdy tylko wrócę do tego projektu. – Disasterkid