Fastest Way of Inserting in Entity Framework
我正在寻找插入实体框架的最快方法。
我之所以这样问是因为您有一个活动的TransactionScope,并且插入量很大(4000多个)。它可能持续10分钟以上(事务的默认超时),这将导致不完整的事务。
你在问题评论中的评论:
"...SavingChanges (for each
record)..."
这是你能做的最糟糕的事!为每个记录调用
- 所有记录后打一次电话给
SaveChanges() 。 - 例如100条记录后调用
SaveChanges() 。 - 例如,在100条记录之后调用
SaveChanges() ,然后释放上下文并创建一个新的上下文。 - 禁用更改检测
对于批量插入,我正在使用这样的模式进行工作和试验:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | using (TransactionScope scope = new TransactionScope()) { MyDbContext context = null; try { context = new MyDbContext(); context.Configuration.AutoDetectChangesEnabled = false; int count = 0; foreach (var entityToInsert in someCollectionOfEntitiesToInsert) { ++count; context = AddToContext(context, entityToInsert, count, 100, true); } context.SaveChanges(); } finally { if (context != null) context.Dispose(); } scope.Complete(); } private MyDbContext AddToContext(MyDbContext context, Entity entity, int count, int commitCount, bool recreateContext) { context.Set<Entity>().Add(entity); if (count % commitCount == 0) { context.SaveChanges(); if (recreateContext) { context.Dispose(); context = new MyDbContext(); context.Configuration.AutoDetectChangesEnabled = false; } } return context; } |
我有一个测试程序,它向数据库中插入560000个实体(9个标量属性,没有导航属性)。使用此代码,它可以在3分钟内工作。
对于性能而言,重要的是在"多"记录之后调用
以下是我的560000个实体的一些度量:
- commitCount=1,recreateContext=false:很多小时(这是您当前的过程)
- commitCount=100,recreateContext=false:超过20分钟
- commitCount=1000,recreateContext=false:242秒
- commitCount=10000,recreateContext=false:202秒
- commitCount=100000,recreateContext=false:199秒
- commitCount=1000000,recreateContext=false:内存不足异常
- commitCount=1,recreateContext=true:超过10分钟
- commitCount=10,recreateContext=true:241秒
- commitCount=100,recreateContext=true:164秒
- commitCount=1000,recreateContext=true:191秒
上面第一个测试中的行为是,性能是非常非线性的,并且随着时间的推移会急剧下降。(许多小时是一种估计,我从来没有完成过这个测试,我在20分钟后停在了50000个实体。)这种非线性行为在所有其他测试中都不那么重要。
这种组合可以很好地提高速度。
1 2 | context.Configuration.AutoDetectChangesEnabled = false; context.Configuration.ValidateOnSaveEnabled = false; |
最快的方法是使用我开发的大容量插入扩展。
它使用sqlbackcopy和自定义数据阅读器来获得最大的性能。因此,它比使用常规的insert或addrange快20倍以上
使用非常简单
1 | context.BulkInsert(hugeAmountOfEntities); |
您应该考虑使用
抱歉,我知道您正在寻找一个简单的答案让EF做您想做的,但批量操作并不是真正意义上的ORM。
我同意亚当拉基斯的观点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public static void InsertIntoMembers(DataTable dataTable) { using (var connection = new SqlConnection(@"data source=;persist security info=True;user id=;password=;initial catalog=;MultipleActiveResultSets=True;App=EntityFramework")) { SqlTransaction transaction = null; connection.Open(); try { transaction = connection.BeginTransaction(); using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction)) { sqlBulkCopy.DestinationTableName ="Members"; sqlBulkCopy.ColumnMappings.Add("Firstname","Firstname"); sqlBulkCopy.ColumnMappings.Add("Lastname","Lastname"); sqlBulkCopy.ColumnMappings.Add("DOB","DOB"); sqlBulkCopy.ColumnMappings.Add("Gender","Gender"); sqlBulkCopy.ColumnMappings.Add("Email","Email"); sqlBulkCopy.ColumnMappings.Add("Address1","Address1"); sqlBulkCopy.ColumnMappings.Add("Address2","Address2"); sqlBulkCopy.ColumnMappings.Add("Address3","Address3"); sqlBulkCopy.ColumnMappings.Add("Address4","Address4"); sqlBulkCopy.ColumnMappings.Add("Postcode","Postcode"); sqlBulkCopy.ColumnMappings.Add("MobileNumber","MobileNumber"); sqlBulkCopy.ColumnMappings.Add("TelephoneNumber","TelephoneNumber"); sqlBulkCopy.ColumnMappings.Add("Deleted","Deleted"); sqlBulkCopy.WriteToServer(dataTable); } transaction.Commit(); } catch (Exception) { transaction.Rollback(); } } } |
我研究了斯劳玛的答案(这太棒了,谢谢你的创意人),我减少了批量,直到达到最佳速度。看看斯劳玛的结果:
- commitCount=1,recreateContext=true:超过10分钟
- commitCount=10,recreateContext=true:241秒
- commitCount=100,recreateContext=true:164秒
- commitCount=1000,recreateContext=true:191秒
可以看出,从1到10,从10到100,速度会增加,但从100到1000插入速度又会下降。
因此,我关注的是当您将批大小减少到10到100之间的某个值时会发生什么,下面是我的结果(我使用的是不同的行内容,所以我的时间具有不同的值):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | Quantity | Batch size | Interval 1000 1 3 10000 1 34 100000 1 368 1000 5 1 10000 5 12 100000 5 133 1000 10 1 10000 10 11 100000 10 101 1000 20 1 10000 20 9 100000 20 92 1000 27 0 10000 27 9 100000 27 92 1000 30 0 10000 30 9 100000 30 92 1000 35 1 10000 35 9 100000 35 94 1000 50 1 10000 50 10 100000 50 106 1000 100 1 10000 100 14 100000 100 141 |
根据我的结果,批量大小的实际最佳值约为30。小于10和100。问题是,我不知道为什么30是最佳的,也不可能找到任何合理的解释。
我将推荐这篇关于如何使用ef进行批量插入的文章。
实体框架和缓慢批量插入
他探索了这些领域并比较了表现:
正如其他人所说,如果您真的想要很好的插入性能,那么sqlblkcopy就是这样做的方法。
实现起来有点麻烦,但是有一些库可以帮助您完成它。外面有一些,但这次我会无耻地插入我自己的库:https://github.com/mikaeleliasson/entityframework.utilities批量插入实体
您需要的唯一代码是:
1 2 3 4 |
那要快多少?很难说,因为它取决于许多因素,如计算机性能、网络、对象大小等。我做的性能测试表明,如果您像其他答案中提到的那样优化您的ef配置,可以在本地主机上以大约10秒的标准方式插入25000个实体。使用大约300毫秒的efutilities,更有趣的是,我用这个方法在15秒内节省了大约300万个实体,平均每秒节省了大约20万个实体。
当然,如果需要插入相关的数据,则有一个问题。这可以使用上述方法在SQL Server中有效地完成,但它要求您具有一个ID生成策略,该策略允许您在父级的应用程序代码中生成ID,以便设置外键。这可以使用guid或类似hilo id生成的东西来完成。
如果您的实体依赖于上下文中的其他预加载实体(如导航属性),则
我使用类似的概念来保持我的上下文较小,以实现相同的性能。
但是,我只是简单地分离已经存在于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public void AddAndSave<TEntity>(List<TEntity> entities) where TEntity : class { const int CommitCount = 1000; //set your own best performance number here int currentCount = 0; while (currentCount < entities.Count()) { //make sure it don't commit more than the entities you have int commitCount = CommitCount; if ((entities.Count - currentCount) < commitCount) commitCount = entities.Count - currentCount; //e.g. Add entities [ i = 0 to 999, 1000 to 1999, ... , n to n+999... ] to conext for (int i = currentCount; i < (currentCount + commitCount); i++) _context.Entry(entities[i]).State = System.Data.EntityState.Added; //same as calling _context.Set<TEntity>().Add(entities[i]); //commit entities[n to n+999] to database _context.SaveChanges(); //detach all entities in the context that committed to database //so it won't overload the context for (int i = currentCount; i < (currentCount + commitCount); i++) _context.Entry(entities[i]).State = System.Data.EntityState.Detached; currentCount += commitCount; } } |
如果需要的话,用Try-Catch和
我知道这是一个非常古老的问题,但是这里的一个人说开发了一个扩展方法来将批量插入与ef结合使用,当我检查时,我发现这个库今天花费了599美元(对于一个开发人员)。也许这对整个库都是有意义的,但是对于批量插入来说,这太多了。
这里有一个非常简单的扩展方法。我首先将它与数据库结合使用(不要先用代码进行测试,但我认为这同样有效)。根据上下文名称更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | public partial class YourEntities : DbContext { public async Task BulkInsertAllAsync<T>(IEnumerable<T> entities) { using (var conn = new SqlConnection(Database.Connection.ConnectionString)) { await conn.OpenAsync(); Type t = typeof(T); var bulkCopy = new SqlBulkCopy(conn) { DestinationTableName = GetTableName(t) }; var table = new DataTable(); var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string)); foreach (var property in properties) { Type propertyType = property.PropertyType; if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { propertyType = Nullable.GetUnderlyingType(propertyType); } table.Columns.Add(new DataColumn(property.Name, propertyType)); } foreach (var entity in entities) { table.Rows.Add( properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray()); } bulkCopy.BulkCopyTimeout = 0; await bulkCopy.WriteToServerAsync(table); } } public void BulkInsertAll<T>(IEnumerable<T> entities) { using (var conn = new SqlConnection(Database.Connection.ConnectionString)) { conn.Open(); Type t = typeof(T); var bulkCopy = new SqlBulkCopy(conn) { DestinationTableName = GetTableName(t) }; var table = new DataTable(); var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string)); foreach (var property in properties) { Type propertyType = property.PropertyType; if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { propertyType = Nullable.GetUnderlyingType(propertyType); } table.Columns.Add(new DataColumn(property.Name, propertyType)); } foreach (var entity in entities) { table.Rows.Add( properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray()); } bulkCopy.BulkCopyTimeout = 0; bulkCopy.WriteToServer(table); } } public string GetTableName(Type type) { var metadata = ((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace; var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace)); var entityType = metadata .GetItems<EntityType>(DataSpace.OSpace) .Single(e => objectItemCollection.GetClrType(e) == type); var entitySet = metadata .GetItems<EntityContainer>(DataSpace.CSpace) .Single() .EntitySets .Single(s => s.ElementType.Name == entityType.Name); var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace) .Single() .EntitySetMappings .Single(s => s.EntitySet == entitySet); var table = mapping .EntityTypeMappings.Single() .Fragments.Single() .StoreEntitySet; return (string)table.MetadataProperties["Table"].Value ?? table.Name; } } |
您可以将其用于继承
1 | await context.BulkInsertAllAsync(items); |
尝试使用将获取要插入数据的XML的存储过程。
I'm looking for the fastest way of inserting into Entity Framework
有些第三方库支持大容量插入:
- z.entityframework.extensions(推荐)
- 遗赠
- EntityFramework.BulkInsert
请参见:实体框架大容量插入库
选择大容量插入库时要小心。只有实体框架扩展支持所有类型的关联和继承,而且它是唯一仍受支持的。
免责声明:我是实体框架扩展的所有者
此库允许您执行场景所需的所有批量操作:
- 批量保存更改
- 批量插入
- 批量删除
- 批量更新
- 批量合并
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // Easy to use context.BulkSaveChanges(); // Easy to customize context.BulkSaveChanges(bulk => bulk.BatchSize = 100); // Perform Bulk Operations context.BulkDelete(customers); context.BulkInsert(customers); context.BulkUpdate(customers); // Customize Primary Key context.BulkMerge(customers, operation => { operation.ColumnPrimaryKeyExpression = customer => customer.Code; }); |
我对上面的@slauma示例进行了一般扩展;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public static class DataExtensions { public static DbContext AddToContext<T>(this DbContext context, object entity, int count, int commitCount, bool recreateContext, Func<DbContext> contextCreator) { context.Set(typeof(T)).Add((T)entity); if (count % commitCount == 0) { context.SaveChanges(); if (recreateContext) { context.Dispose(); context = contextCreator.Invoke(); context.Configuration.AutoDetectChangesEnabled = false; } } return context; } } |
用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public void AddEntities(List<YourEntity> entities) { using (var transactionScope = new TransactionScope()) { DbContext context = new YourContext(); int count = 0; foreach (var entity in entities) { ++count; context = context.AddToContext<TenancyNote>(entity, count, 100, true, () => new YourContext()); } context.SaveChanges(); transactionScope.Complete(); } } |
保存列表的最快方法之一必须应用以下代码
1 2 | context.Configuration.AutoDetectChangesEnabled = false; context.Configuration.ValidateOnSaveEnabled = false; |
autodetectChangesEnabled=false
添加、添加范围和保存更改:不检测更改。
validateOnSaveEnabled=false;
未检测到更改跟踪程序
你必须添加nuget
1 | Install-Package Z.EntityFramework.Extensions |
现在您可以使用以下代码
1 2 3 4 5 6 7 | var context = new MyContext(); context.Configuration.AutoDetectChangesEnabled = false; context.Configuration.ValidateOnSaveEnabled = false; context.BulkInsert(list); context.BulkSaveChanges(); |
因为这里从来没有提到过,所以我想在这里重新推荐efcore.bulkextensions。
1 2 3 4 5 6 | context.BulkInsert(entitiesList); context.BulkInsertAsync(entitiesList); context.BulkUpdate(entitiesList); context.BulkUpdateAsync(entitiesList); context.BulkDelete(entitiesList); context.BulkDeleteAsync(entitiesList); context.BulkInsertOrUpdate(entitiesList); context.BulkInsertOrUpdateAsync(entitiesList); // Upsert context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); // Sync context.BulkRead(entitiesList); context.BulkReadAsync(entitiesList); |
下面是使用实体框架和使用sqlbackcopy类之间的性能比较:如何将复杂对象大容量插入到SQL Server数据库中
正如其他人已经强调的那样,ORM并不打算用于批量操作。它们提供了灵活性、关注点分离和其他好处,但批量操作(批量读取除外)不是其中之一。
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | void BulkInsert(GpsReceiverTrack[] gpsReceiverTracks) { if (gpsReceiverTracks == null) { throw new ArgumentNullException(nameof(gpsReceiverTracks)); } DataTable dataTable = new DataTable("GpsReceiverTracks"); dataTable.Columns.Add("ID", typeof(int)); dataTable.Columns.Add("DownloadedTrackID", typeof(int)); dataTable.Columns.Add("Time", typeof(TimeSpan)); dataTable.Columns.Add("Latitude", typeof(double)); dataTable.Columns.Add("Longitude", typeof(double)); dataTable.Columns.Add("Altitude", typeof(double)); for (int i = 0; i < gpsReceiverTracks.Length; i++) { dataTable.Rows.Add ( new object[] { gpsReceiverTracks[i].ID, gpsReceiverTracks[i].DownloadedTrackID, gpsReceiverTracks[i].Time, gpsReceiverTracks[i].Latitude, gpsReceiverTracks[i].Longitude, gpsReceiverTracks[i].Altitude } ); } string connectionString = (new TeamTrackerEntities()).Database.Connection.ConnectionString; using (var connection = new SqlConnection(connectionString)) { connection.Open(); using (var transaction = connection.BeginTransaction()) { using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction)) { sqlBulkCopy.DestinationTableName = dataTable.TableName; foreach (DataColumn column in dataTable.Columns) { sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName); } sqlBulkCopy.WriteToServer(dataTable); } transaction.Commit(); } } return; } |
另一种选择是使用Nuget提供的sqlblktools。它非常容易使用,并且具有一些强大的功能。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | var bulk = new BulkOperations(); var books = GetBooks(); using (TransactionScope trans = new TransactionScope()) { using (SqlConnection conn = new SqlConnection(ConfigurationManager .ConnectionStrings["SqlBulkToolsTest"].ConnectionString)) { bulk.Setup<Book>() .ForCollection(books) .WithTable("Books") .AddAllColumns() .BulkInsert() .Commit(conn); } trans.Complete(); } |
有关更多示例和高级用法,请参阅文档。免责声明:我是本图书馆的作者,任何观点都是我自己的观点。
这里编写的所有解决方案都没有帮助,因为当您执行savechanges()时,insert语句将逐个发送到数据库,这就是实体的工作方式。
例如,如果您访问数据库和返回数据库的时间是50毫秒,那么插入所需的时间是记录数x 50毫秒。
您必须使用BulkInsert,链接如下:https://efbulkInsert.codeplex.com/
我用它把插入时间从5-6分钟缩短到10-12秒。
[PostgreSQL的新解决方案]嘿,我知道这是一篇很老的文章,但我最近遇到了类似的问题,但我们使用的是PostgreSQL。我想使用有效的Bulkinsert,结果很困难。我没有找到任何合适的免费图书馆来做这个数据库。我只找到这个助手:https://bytefish.de/blog/postgresql_批量插入/也在Nuget上。我编写了一个小的映射器,它按照实体框架的方式自动映射属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | public static PostgreSQLCopyHelper<T> CreateHelper<T>(string schemaName, string tableName) { var helper = new PostgreSQLCopyHelper<T>("dbo",""" + tableName +"""); var properties = typeof(T).GetProperties(); foreach(var prop in properties) { var type = prop.PropertyType; if (Attribute.IsDefined(prop, typeof(KeyAttribute)) || Attribute.IsDefined(prop, typeof(ForeignKeyAttribute))) continue; switch (type) { case Type intType when intType == typeof(int) || intType == typeof(int?): { helper = helper.MapInteger(""" + prop.Name +""", x => (int?)typeof(T).GetProperty(prop.Name).GetValue(x, null)); break; } case Type stringType when stringType == typeof(string): { helper = helper.MapText(""" + prop.Name +""", x => (string)typeof(T).GetProperty(prop.Name).GetValue(x, null)); break; } case Type dateType when dateType == typeof(DateTime) || dateType == typeof(DateTime?): { helper = helper.MapTimeStamp(""" + prop.Name +""", x => (DateTime?)typeof(T).GetProperty(prop.Name).GetValue(x, null)); break; } case Type decimalType when decimalType == typeof(decimal) || decimalType == typeof(decimal?): { helper = helper.MapMoney(""" + prop.Name +""", x => (decimal?)typeof(T).GetProperty(prop.Name).GetValue(x, null)); break; } case Type doubleType when doubleType == typeof(double) || doubleType == typeof(double?): { helper = helper.MapDouble(""" + prop.Name +""", x => (double?)typeof(T).GetProperty(prop.Name).GetValue(x, null)); break; } case Type floatType when floatType == typeof(float) || floatType == typeof(float?): { helper = helper.MapReal(""" + prop.Name +""", x => (float?)typeof(T).GetProperty(prop.Name).GetValue(x, null)); break; } case Type guidType when guidType == typeof(Guid): { helper = helper.MapUUID(""" + prop.Name +""", x => (Guid)typeof(T).GetProperty(prop.Name).GetValue(x, null)); break; } } } return helper; } |
我使用它的方式如下(我有实体命名为承诺):
1 2 | var undertakingHelper = BulkMapper.CreateHelper<Model.Undertaking>("dbo", nameof(Model.Undertaking)); undertakingHelper.SaveAll(transaction.UnderlyingTransaction.Connection as Npgsql.NpgsqlConnection, undertakingsToAdd)); |
我展示了一个事务示例,但也可以通过从上下文中检索到的正常连接来完成。underakingstoad是正常实体记录的可枚举项,我希望将这些记录批量插入到DB中。
这个解决方案,经过几个小时的研究和尝试,我已经得到了,正如你所期望的,更快,最后易于使用和免费!我真的建议您使用这个解决方案,不仅因为上面提到的原因,而且因为它是唯一一个我对PostgreSQL本身没有问题的解决方案,许多其他的解决方案都可以完美地工作,例如使用sqlserver。
sqlblkcopy非常快
这是我的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | // at some point in my calling code, I will call: var myDataTable = CreateMyDataTable(); myDataTable.Rows.Add(Guid.NewGuid,tableHeaderId,theName,theValue); // e.g. - need this call for each row to insert var efConnectionString = ConfigurationManager.ConnectionStrings["MyWebConfigEfConnection"].ConnectionString; var efConnectionStringBuilder = new EntityConnectionStringBuilder(efConnectionString); var connectionString = efConnectionStringBuilder.ProviderConnectionString; BulkInsert(connectionString, myDataTable); private DataTable CreateMyDataTable() { var myDataTable = new DataTable { TableName ="MyTable"}; // this table has an identity column - don't need to specify that myDataTable.Columns.Add("MyTableRecordGuid", typeof(Guid)); myDataTable.Columns.Add("MyTableHeaderId", typeof(int)); myDataTable.Columns.Add("ColumnName", typeof(string)); myDataTable.Columns.Add("ColumnValue", typeof(string)); return myDataTable; } private void BulkInsert(string connectionString, DataTable dataTable) { using (var connection = new SqlConnection(connectionString)) { connection.Open(); SqlTransaction transaction = null; try { transaction = connection.BeginTransaction(); using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction)) { sqlBulkCopy.DestinationTableName = dataTable.TableName; foreach (DataColumn column in dataTable.Columns) { sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName); } sqlBulkCopy.WriteToServer(dataTable); } transaction.Commit(); } catch (Exception) { transaction?.Rollback(); throw; } } } |
据我所知,
在这种情况下,您可以在
您可以使用批量包库。在实体框架大于等于6.0.0的项目中使用大容量插入1.0.0版本。
在这里可以找到更多的描述-批量操作源代码
您是否尝试过通过后台工作人员或任务插入?
在我的例子中,我插入了7760个寄存器,分布在182个具有外键关系的不同表中(通过navigationproperties)。
没有这个任务,花了2分半钟。在一个任务(
我只在将所有实体添加到上下文之后执行
使用以XML形式接收输入数据的存储过程来插入数据。
从C代码传递中,以XML形式插入数据。
例如,在C中,语法如下:
1 | object id_application = db.ExecuteScalar("procSaveApplication", xml) |
但是,对于超过(+4000)个插入,我建议使用存储过程。附加已用时间。我确实在20"中插入了11.788行。
这就是IT代码
1 2 3 4 5 6 7 |
秘密是插入到相同的空白临时表中。插页很快就变亮了。然后在主大表中运行一个插入。然后截断准备好下一批处理的临时表。
IE.
1 2 3 4 5 6 | insert into some_staging_table using Entity Framework. -- Single insert into main table (this could be a tiny stored proc call) insert into some_main_already_large_table (columns...) select (columns...) from some_staging_table truncate table some_staging_table |
使用此技术可以提高在实体框架中插入记录的速度。在这里,我使用一个简单的存储过程来插入记录。为了执行这个存储过程,我使用了执行原始SQL的实体框架的.fromSQL()方法。
存储过程代码:1 2 3 4 5 6 7 | CREATE PROCEDURE TestProc @FirstParam VARCHAR(50), @SecondParam VARCHAR(50) AS Insert into SomeTable(Name, Address) values(@FirstParam, @SecondParam) GO |
接下来,循环遍历所有4000条记录,并添加执行存储的
每100个循环执行一次过程。
为此,我创建了一个字符串查询来执行这个过程,并继续向它追加每一组记录。
然后检查循环是否以100的倍数运行,在这种情况下,使用
So for 4000 records I only have to execute the procedure for only
4000/100 = 40 times.
检查以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 | string execQuery =""; var context = new MyContext(); for (int i = 0; i < 4000; i++) { execQuery +="EXEC TestProc @FirstParam = 'First'" + i +"'', @SecondParam = 'Second'" + i +"''"; if (i % 100 == 0) { context.Student.FromSql(execQuery); execQuery =""; } } |