C# 实体框架6 DbSet AddRange与IDbSet Add-AddRange怎么能快这么多?

C# 实体框架6 DbSet AddRange与IDbSet Add-AddRange怎么能快这么多?,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,我在家用电脑上玩EntityFramework6,决定尝试插入相当多的行,大约430k 我的第一次尝试看起来是这样的,是的,我知道它可以更好,但无论如何它是为了研究: var watch = System.Diagnostics.Stopwatch.StartNew(); foreach (var event in group) { db.Events.Add(event); db.SaveChanges(); } var dbCount = db.Events.Count(x

我在家用电脑上玩EntityFramework6,决定尝试插入相当多的行,大约430k

我的第一次尝试看起来是这样的,是的,我知道它可以更好,但无论如何它是为了研究:

var watch = System.Diagnostics.Stopwatch.StartNew();
foreach (var event in group)
{
    db.Events.Add(event);
    db.SaveChanges();
}

var dbCount = db.Events.Count(x => x.ImportInformation.FileName == group.Key);

if (dbCount != group.Count())
{
    throw new Exception("Mismatch between rows added for file and current number of rows!");
}

watch.Stop();
Console.WriteLine($"Added {dbCount} events to database in {watch.Elapsed.ToString()}");
晚上开始,下班回家后再检查。结果是:

如您所见,在最初的4小时41分钟内添加了64523个事件,但随后速度变慢,接下来的66985个事件花费了14小时51分钟。我检查了数据库,程序仍在插入事件,但速度极低。然后,我决定尝试DbSet的“新”方法

我将我的模型从
IDbSet
切换到
DbSet
,并用以下内容替换
foreach
循环:

db.Events.AddRange(group);
db.SaveChanges();


我现在可以在大约30秒内添加60k+个事件。这也许并不快,但仍然是一个巨大的进步。为了实现这一目标,发动机罩下发生了什么?我原以为明天我会检查SQL Server Profiler以获取查询,但最好能解释一下代码中发生了什么。

正如Jakub回答的那样,在每个添加的实体都无效后调用SaveChanges。但是,即使您将其移出,您仍然会遇到一些性能问题。这将不会修复Add方法引起的性能问题

添加vs添加范围 使用Add方法添加多个实体是一个非常常见的错误。事实上,DetectChanges方法的速度非常慢

  • Add方法在添加每个记录后检测更改
  • AddRange方法在添加所有记录后检测更改
见:


它可能不快,但仍然是一个巨大的改进

可以使性能非常接近SqlBulkCopy

免责声明:我是项目的所有者

(这个图书馆不是免费的)

该库允许您一次保存多个实体,从而使您的代码更加高效。支持所有批量操作:

  • 批量保存更改
  • 隔板
  • 批量更新
  • 批量删除
  • 大合并
  • 批量同步
例如:

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

这不是因为
Add
AddRange
,而是因为带有
Add
的版本正在为每个事件调用
db.SaveChanges()
。如果将其移出循环,结果应该类似。