这是我目前正在处理的以下设置:
- 我有一个 sql 表,其中包含 ~1.000.000 个条目,我需要更新
- 更新发生在一个单独的线程中(由任务启动)
- 由于线程中的内存有限,我以每批 1000 个条目的批处理处理列表 (在主线程的测试项目中运行/没有任何任务时,没有 OOM 异常)
UpdateList()
函数更新列表的字段或为该表或 DbContext 中的其他表创建新记录- 在
Process_Failure()
函数中,我有一个用于整个列表的上下文实例 - 在
Process_Success()
函数中,我将while
循环移到了上下文之外
private void Process_Success()
{
var totalProcessedCounter = 0;
while( true )
{
using( var context = new MyDbContext() )
{
var list = context.MyClass.OrderBy( x => x.Id )
.Skip( totalProcessedCounter ).Take( 1000 )
.ToList();
if( !list.Any() )
break;
UpdateList( list );
totalProcessedCounter += list.Count;
}
}
}
private void Process_Failure()
{
var totalProcessedCounter = 0;
using( var context = new MyDbContext() )
{
while( true )
{
var list = context.MyClass.OrderBy( x => x.Id )
.Skip( totalProcessedCounter ).Take( 1000 )
.ToList(); // throws OutOfMemoryException
if( !list.Any() )
break;
UpdateList( list );
totalProcessedCounter += list.Count;
}
}
}
private void UpdateList( List<MyClass> list )
{
var doSaveChanges = false;
list = list.Where( x => SomeFilter( x ) ).ToList();
using( var context = new MyDbContext() )
{
foreach( var item in list )
{
ChangeItem( item );
}
if( doSaveChanges )
context.SaveChanges();
}
}
当我在 UpdateList()
的嵌套函数调用中创建另一个实例时,以某种方式污染/填充了 context
?
最佳答案
之所以得到异常是因为DbContext
缓存了the data you read from DB ,因此在某些时候,如果您不断向其缓存中添加实体,您将炸毁内存,并且您将获得 OutOfMemoryException
。实体不会被 GC 清除,因为它们正被 DbContext
引用。
尝试使用 .AsNoTracking()
:
private void Process_NoTracking()
{
var totalProcessedCounter = 0;
using( var context = new MyDbContext() )
{
while( true )
{
var list = context
.MyClass
.AsNoTracking()
.OrderBy( x => x.Id )
.Skip( totalProcessedCounter )
.Take( 1000 )
.ToList();
if( !list.Any() )
break;
UpdateList( list );
totalProcessedCounter += list.Count;
}
}
}
但如果您不跟踪实体,更新它们 is more difficult (阅读“将现有实体附加到上下文”),因为这些实体不属于任何上下文并且不会被跟踪。
我不会将 EF 用于这样的事情,这对于 UPDATE/SELECT SQL 语句来说似乎是一个很好的任务。
关于c# - 内存不足异常 : using DbContext in Task,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39464913/