entity framework批量插入引发KeyNotFoundException错误

我使用的是AddRange() ,由于AddRange()方法速度慢,我需要使用BulkInsert 。 所以我通过这里添加了针对EF6的BulkInsert的NuGet包。

添加dll后我收到的第一件事就是这个警告:

发现同一依赖程序集的不同版本之间存在冲突。 请在项目文件中将“AutoGenerateBindingRedirects”属性设置为true。

List了所有我的Contact实体,即需要添加的contactsToInsert(我的联系人在另一个表中也有一个外键)。 当我尝试运行以下代码时,我收到一个KeyNotFoundException ,声称“ 给定的密​​钥不在字典中 ”。

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

NB。 我在BackgroundWorker运行BulkInsert。 这可能是导致问题的原因吗?

堆栈跟踪:

  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) 

通过修改此博客文章中的代码,在遇到BulkInsert()遇到相同的"The given key was not present in the dictionary"错误之后,这对我的Code First Fluent API设置有效。 这里唯一的依赖是在上述post的DataExtensions片段中找到的ToDataTable()扩展方法。

相关部分是GetColumnMappings()方法,它获取POCO类属性的首选名称 (您在代码中指定的名称)作为源列名称(在enumerable-turned-datatable中)并将其与元数据成员的名称 (DB)配对列名称)作为目标列名称。

GetColumnMappings():

 private IEnumerable GetColumnMappings() { 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)(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)); } 

完整代码:

 // 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(IEnumerable items) where T : class { EnsureOpenConnection(); using (var bulkCopy = new SqlBulkCopy((SqlConnection)connection) { DestinationTableName = GetTableName(), }) { foreach (var mapping in GetColumnMappings()) { 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() where T : class { string sql = objectContext.CreateObjectSet().ToTraceString(); Regex regex = new Regex("FROM (?.*) AS"); Match match = regex.Match(sql); string table = match.Groups["table"].Value; return table; } private IEnumerable GetColumnMappings() { 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)(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)); } }

好吧,我有一个相同的错误无法在网上找到任何答案,所以不得不看起来很深,所以这里是我想:

当你的实体有一个inheritance而子实体如果没有被定义为DBSet的一部分时它会标记这样的错误,其次,不确定为什么它期望新的DbContext只有用于bulkInsertions的相关实体,如果有任何其他实体它引发了同样的错误。

所以它的第二个原因所以我必须修复这两个,并像马一样跑!

值得一试,所以试一试

“BulkInsert”库非常快,但不是很灵活且不受支持。

它不支持所有类型的inheritance(TPC,TPT),并且在列映射方面存在一些问题。

您遇到的问题是出于以下原因之一。

免责声明 :我是项目entity framework扩展的所有者

该库是性能的终极库,允许:

  • BulkSaveChanges
  • BulkInsert
  • BulkUpdate
  • BulkDelete
  • BulkMerge

支持所有inheritance和关联。

例:

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