我正在使用EF6
,由于AddRange()
方法的速度较慢,我需要使用BulkInsert
。所以我通过here为EF6添加了BulkInsert的NuGet包。
添加dll
后我收到的第一件事就是这个警告:
发现同一依赖的不同版本之间存在冲突 部件。请将“AutoGenerateBindingRedirects”属性设置为 在项目文件中为true。
我创建了List
个Contact
个实体,即需要添加的 contactsToInsert (我的联系人在另一个表中也有外键)。当我尝试运行以下代码时,我收到KeyNotFoundException
声称“字典中没有给定密钥”。
using (var db = new Entities(myConnectionString))
{
db.BulkInsert(contactsToInsert);
db.SaveChanges();
}
NB。我在BackgroundWorker
内运行BulkInsert。这可能是this fix判断问题的原因吗?
堆栈跟踪:
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)
答案 0 :(得分:2)
通过修改this blog post中的代码,在"The given key was not present in the dictionary"
遇到相同的BulkInsert()
错误后,我的Code First Fluent API设置有效。这里唯一的依赖是在上述帖子的ToDataTable()
摘要中找到的DataExtensions
扩展方法。
相关部分是GetColumnMappings()
方法,它将POCO类属性的首选名称(您在代码中指定的名称)作为源列名称(在可枚举中) -turned-datatable)并将其与元数据成员的名称(数据库列名称)配对作为目标列名。
<强> 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));
}
完整代码:
// 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));
}
}
答案 1 :(得分:0)
好的,我有同样的错误无法在网上找到任何答案,所以不得不深入了解所以这就是我的想法:
当你的实体有一个继承而子实体如果没有被定义为DBSet的一部分时,它会标记这样的错误,其次,不确定为什么它期望新的DbContext只包含用于bulkInsertions的相关实体(如果有的话)其他实体出现同样的错误。
所以它的第二个原因所以我必须修复这两个,并像马一样跑!
值得一试,所以试一试
答案 2 :(得分:-5)
&#34; BulkInsert&#34;库非常快,但不够灵活且不受支持。
它不支持所有类型的继承(TPC,TPT),并且在列映射方面存在一些问题。
您遇到的问题是出于以下原因之一。
免责声明:我是该项目的所有者Entity Framework Extensions
这个库是性能的终极库,允许:
支持所有继承和关联。
示例:
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();
}