实体框架性能问题,saveChanges非常慢

时间:2014-01-22 02:00:00

标签: entity-framework

最近,我正在做一些简单的EF工作。非常简单, 首先,

List<Book> books = entity.Books.WHERE(c=>c.processed==false)ToList();  
then  
    foreach(var book in books)  
    {  
      //DoSomelogic, update some properties,   
    book.ISBN = "ISBN " + randomNumber();  
    book.processed = true;  
    entity.saveChanges(book)  
    }  

我将entity.saveChanges放在foreach中,因为它是一个大型列表,大约有10万条记录,如果这条记录处理没有问题,则标记此记录,设置book.processed = true,如果该过程被异常中断,然后下次,我不必再次处理这些好的记录。

一切似乎都对我好。处理数百条记录时速度很快。然后当我们移动到100k记录时,entity.saveChanges非常慢。每条记录约1-3秒。然后我们保留实体模型,但用经典entity.saveChanges替换SqlHelper.ExecuteNonQuery("update_book", sqlparams)。它非常快。

有谁能告诉我为什么实体框架进程那么慢?如果我仍然想使用entity.saveChanges,那么提高性能的最佳方法是什么?

谢谢

7 个答案:

答案 0 :(得分:31)

在执行插入操作之前关闭更改跟踪。这将显着改善您的表现(秩序的大小)。将SaveChanges()置于循环之外也会有所帮助,但关闭更改跟踪将会有所帮助。

using (var context = new CustomerContext())
{
    context.Configuration.AutoDetectChangesEnabled = false;

    // A loop to add all your new entities

    context.SaveChanges();
}

有关详情,请参阅this page

答案 1 :(得分:7)

我会把foreChanges(书)放在foreach之外。由于book是作为一个列表在实体上,你可以将它放在外面,EF可以更好地使用生成的代码。

列表是实体的属性,EF旨在优化后端数据库上的更新/创建/删除。如果你这样做,我会好奇它是否有帮助。

答案 2 :(得分:4)

我也建议你将SaveChanges()从循环中取出,因为它对数据库进行了大量的更新,因此上下文将有'n'次迭代检查点和所需的验证。

var books = entity.Books.Where(c => c.processed == false).ToList();

books.Foreach(b =>
{
    b.ISBN = "ISBN " + randomNumber();
    b.processed = true;
    //DoSomelogic, update some properties  
});
entity.SaveChanges();

答案 3 :(得分:0)

&#34;的 AsNoTracking &#34;适合我

前:

Item itemctx = ctx.Items.AsNoTracking().Single(i=>i.idItem == item.idItem);
ctx.Entry(itemctx).CurrentValues.SetValues(item);
itemctx.images = item.images;
ctx.SaveChanges();

没有&#34; AsNoTracking&#34;更新非常缓慢。

答案 4 :(得分:0)

在我看来,从性能和内存消耗的角度来看,实体框架对于BULK操作来说是一个糟糕的选择。一旦超过几千条记录,SaveChanges方法就会真正开始崩溃。

您可以尝试将您的工作划分为较小的交易,但同样,我认为您正在努力创建此工作。

更好的方法是利用DBMS已经提供的BULK操作。 SQL Server通过.NET提供BULK COPY。 Oracle为Oracle.DataAccess或非托管数据访问提供BULK COPY。对于Oracle.ManagedDataAccess,遗憾的是,BULK COPY库不可用。但我可以使用BULK COLLECT / FOR ALL创建一个Oracle存储过程,允许我在几秒钟内插入数千条记录,并在应用程序中占用更少的内存。在.NET应用程序中,您可以将PLSQL关联数组实现为参数等。

利用DBMS中的BULK工具的好处是减少应用程序,查询处理器和数据库引擎之间的上下文切换。

我确定其他数据库供应商提供类似的东西。

答案 5 :(得分:0)

使用此Nuget软件包: Z.EntityFramework.Extensions

它具有可在DbContext上调用的扩展方法,例如DbContext.BulkSaveChanges,其运行速度惊人。

答案 6 :(得分:0)

我只是直接执行插入命令。

//the Id property is the primary key, so need to have this update automatically    
db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT[dbo].[MyTable] ON");
                            
foreach (var p in itemsToSave)
{
    db.Database.ExecuteSqlCommand("INSERT INTO[dbo].[MyTable]([Property1], [Property2]) VALUES(@Property1, @Property2)",
        new SqlParameter("@Property1", p.Property1),
        new SqlParameter("@Property2", p.Property2)"
}

db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT[dbo].[MyTable] OFF");

工作真的很快。 EF 在批量更新时慢得令人难以置信,在我的情况下完全无法用于十多个项目。