我正在尝试使用EF增强将数千行写入数据库的性能。我发现SaveChanges()的速度会在单个上下文中随着时间的推移而降低,并且在n个插入之后处理和重新创建上下文可以帮助这个。
using(Context context = new Context())
{
for(int i = 0; i < numberOfLines; i++)
{
context.Collection.AddObject(line);
if (i % 100 == 0)
{
context.SaveChanges()
// Here I would like to call dispose on context and then create a new one
}
}
}
当然,我需要使用或做类似的事情,因为还有很多事情要发生,我需要确保在任何时候抛出异常时都会调用dispose。
有什么想法吗?我知道我可以在try块之外声明上下文,然后使用context.Dispose()获得finally。我显然不想这样做。
编辑:我意识到我发布的代码片段并没有完全显示我为什么要这样做。 for循环在IF语句之外的其他位置调用context.SaveChanges()。因此,当IF评估为true时,我已多次调用SaveChanges()。
答案 0 :(得分:5)
听起来你基本上想要批量处理一个集合。使用MoreLINQ(also on NuGet):
这很简单foreach (var batch in dataToUpload.Batch(100))
{
using (var context = new Context())
{
foreach (var item in batch)
{
...
}
context.SaveChanges();
}
}
除了其他任何内容之外,描述你想要实现的目标(IMO)要比使用一个检查i % 100
的循环更清楚。
(http://morelinq.googlecode.com的原始链接不再有效,替换为当前的GitHub链接)
答案 1 :(得分:3)
由于using
内的标识符是只读的,因此您不能将其设置为新对象。我会说最好的选择就是按照你说的你不想做的事情来做,并使用try/finally
代替使用:
Context context = null;
try
{
context = new Context();
for (int i = 0; i < numberOfLines; i++)
{
context.Collection.AddObject(line);
if (i % 100 == 0)
{
context.SaveChanges();
context.Dispose();
context = new Context();
}
}
}
finally { context.Dispose(); }
虽然你可以通过创造一个包裹另一个一次性物体的新的一次性物体来技术上做你想要的,并允许包裹的物体发生变异,但这将是一个糟糕的做法。 C#特别是不受限制地使给定的标识符成为只读,因为在using
块期间该对象的更改特别令人困惑,并且它使代码更难以理由,但如果你真的想这样做,你可以:
using (var context = new EvilMutableDisposableWrapper<Context>(new Context()))
{
for (int i = 0; i < numberOfLines; i++)
{
context.Wrapped.Collection.AddObject(line);
if (i % 100 == 0)
{
context.Wrapped.SaveChanges();
context.Dispose();
context.Wrapped = new Context();
}
}
}
请不要这样做。
答案 2 :(得分:1)
这是怎么回事:
const int ChunkSize = 100;
for(int i = 0; i < numberOfLines; i += ChunkSize)
{
using(Context context = new Context())
{
for(int j = i; j < i + ChunkSize && j < numberOfLines; j++)
{
// if needed, use j within this loop, not i
context.Collection.AddObject(line);
}
context.SaveChanges()
}
}