如何加速DbSet.Add()?

Tam*_*man 30 c# performance entity-framework sql-server-2008

我必须从CSV文件导入大约30k行到我的SQL数据库,这可能需要20分钟.

使用分析器进行故障排除向我显示DbSet.Add占用的时间最多,但为什么呢?

我有这些Entity Framework Code-First类:

public class Article
{
    // About 20 properties, each property doesn't store excessive amounts of data
}

public class Database : DbContext
{
    public DbSet<Article> Articles { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

对于我的for循环中的每个项目,我做:

db.Articles.Add(article);
Run Code Online (Sandbox Code Playgroud)

在for循环之外我做:

db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

它与我的本地SQLExpress服务器连接,但我想在SaveChanges被调用之前没有任何东西,所以我猜服务器不会出现问题....

Rud*_*udi 46

根据Kevin Ramen的评论(3月29日),我可以确认这一设置db.Configuration.AutoDetectChangesEnabled = false对速度产生巨大影响

运行Add()在2324项默认情况下跑了我的机器上3分钟15秒,禁用自动检测导致操作完成0.5秒.

http://blog.larud.net/archive/2011/07/12/bulk-load-items-to-a-ef-4-1-code-first-aspx


spo*_*ida 19

我将添加Kervin Ramen的评论,说如果你只是进行插入(没有更新或删除),那么你可以在对上下文进行任何插入之前安全地设置以下属性:

DbContext.Configuration.AutoDetectChangesEnabled = false;
DbContext.Configuration.ValidateOnSaveEnabled = false;
Run Code Online (Sandbox Code Playgroud)

我在工作中遇到了一次性批量导入问题.如果不设置上述属性,则向上下文添加大约7500个复杂对象需要花费30多分钟.设置上述属性(禁用EF检查和更改跟踪)会将导入减少到秒.

但是,我再次强调,如果你正在插入,只使用它.如果需要将插入与更新/删除混合,则可以将代码拆分为两个路径,并禁用插入部分的EF检查,然后重新启用对更新/删除路径的检查.我成功地使用这种方法来解决缓慢的DbSet.Add()行为.


Mar*_*ell 9

工作单元中的每个项目都有开销,因为它必须检查(和更新)身份管理器,添加到各种集合等.

我要尝试的第一件事就是批量打入500组(改变那个数字以适应),每次都从一个新的(新的)对象上下文开始 - 否则你可以合理地期望伸缩性能.将其分成批次也可以防止巨石交易使一切停止.

除此之外; SqlBulkCopy的.它专为大型进口而设计,开销最小.但它不是EF.

  • 小组的建议使它快一点,但它的工作速度不够快.在通过讨厌的错误进行了一些迭代之后,我得到了SqlBulkCopy的工作,但这是令人讨厌的代码,但它的工作原理.可能会重构它或检查它们是否支持稍后批量插入...谢谢Marc和聊天中的人提出了类似的建议!而且,看起来,花了**20分钟**现在需要**2秒**,它的魔力...... (2认同)

Ari*_*hin 5

这里有一个非常容易使用和非常快速的扩展:https : //efbulkinsert.codeplex.com/

它称为“实体框架批量插入”。

扩展本身位于名称空间EntityFramework.BulkInsert.Extensions中。所以要显示扩展方法添加使用

using EntityFramework.BulkInsert.Extensions;
Run Code Online (Sandbox Code Playgroud)

然后你可以做到这一点

context.BulkInsert(entities);
Run Code Online (Sandbox Code Playgroud)

顺便说一句-如果您由于某些原因不希望使用此扩展名,也可以尝试为每篇文章而不运行db.Articles.Add(article),每次创建几篇文章的列表,然后使用AddRange(新EF版本6,以及RemoveRange)将它们一起添加到dbcontext中。