我想用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上运行),只是服务器错误。
我不想用纯SQL(连接到MS SQL Server)执行此操作,但我知道这将是一个解决方案。
答案 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)