无法使用Linq完成大量更新

时间:2014-02-22 06:07:48

标签: performance linq entity-framework azure

我想用LINQ进行大规模更新,例如:

foreach (Message message in db.Messages.ToList())
{
     // Some update on the message object (eg. message.Body = message.Body.Replace(....))
}

db.SaveChanges();

db.Messages是数据库中的所有消息,用EF映射,即:DbSet<Message>

在db中有500.000条消息,似乎前面的代码需要太长时间,所以我无法执行它。它在结束执行之前抛出错误502。没有ASP.NET或C#错误(在Azure上运行),只是服务器错误。

enter image description here

我不想用纯SQL(连接到MS SQL Server)执行此操作,但我知道这将是一个解决方案。

4 个答案:

答案 0 :(得分:0)

你需要分组:

var i = 0;
foreach (Message message in db.Messages.ToList())
{
     if((i % 500) == 0)
     {
     // Some update on the message object (eg. message.Body = message.Body.Replace(....))
         db.SaveChanges();
     }
     else
     {
     // Some update on the message object (eg. message.Body = message.Body.Replace(....))
     }
     i++;
}

db.SaveChanges();

答案 1 :(得分:0)

对于简单的批量更新,请查看EntityFramework.Extended。它的速度更快,因为它为一堆实体创建了一个语句,而不是每个实体创建一个语句。 例如:

//update all tasks with status of 1 to status of 2
context.Tasks.Update(
    t => t.StatusId == 1, 
    t2 => new Task {StatusId = 2});

答案 2 :(得分:0)

这可能是由于DbContext累积了不断增加的Message对象集合,它会跟踪更改。

尝试在循环之前设置db.Configuration.AutoDetectChangesEnabled = false并在之后重新启用,然后在您更改的那些消息上使用db.Entry(message).State = EntityState.Modified。如果对相关对象进行更改,则需要对这些对象执行相同操作,否则在调用db.SaveChanges()时将忽略更改;

关闭AutoDetectChangesEnabled可能会导致一些难以追踪的奇怪副作用,因此最好在大多数情况下保留此功能。只需在熟悉的场景中关闭,即添加大量对象或对大量现有对象进行单独更改。

<强>更新 ...我通常在循环中执行db.SaveChanges()以在db

上传播负载

答案 3 :(得分:0)

最好的方法:SQL。 Check Entity Framework Raw SQL Queries

using (var db = new YourContext()) 
{ 
    var blogs = db.Database.SqlCommand("UPDATE dbo.Messages SET Body = REPLACE(Body, 'pattern', 'replacement'");
}

如果您不想这样做,您应该将工作拆分为块,但是您可能需要为每个块提供不同的上下文,以防止上下文缓存所有已更新的实体。使用Skip / Take组合从数据库中获取正确的项目。

var chunkSize = 500;
int updatedItems;
int changes;

do
{
    using(var db = new YourContext())
    {
        foreach (Message message in db.Messages.Skip(updatedItems).Take(chunkSize).ToList())
        {
            // Some update on the message object (eg. message.Body = message.Body.Replace(....))
        }

        changes = db.SaveChanges();
        updatedItems += changes;
    }
} while(changes > 0)