在dbQuery之后运行OutOfMemoryException

时间:2014-10-29 12:16:25

标签: c# out-of-memory yield-return

我正在运行一个返回大量功能(大约100000)的数据库查询。因为我遇到了上面提到的异常,我试图将查询拆分成几个子查询。但是因此所有这些子查询的结果都被写入方法列表,因此我仍然遇到同样的异常。

所以我想知道在每个子查询完成之后我是否可以通过循环其元素并返回每个子元素来使用yield return

在检索到功能之后,我必须为每一个创建一些新的自定义对象,所以我想知道在这种情况下使用yield return是否会节省内存。

也许以下内容使它更清晰:

foreach (var chunk in IDs.chunk(500))
{       
    List<ComplexObject> result = new List<ComplexObject>():

    // ...
    // make a (sub-)query on every chunk to retrieve 500 objects at once
    // ..

    // now we have up to 500 ComplexObjects within result
    foreach (var parcel in result)
    {
        yield return parcel;
        parcel.Release();       // release COM-object
    }
}

其中chunk是500个元素(ID)的(子)集合。

然后,我在foreach内循环从该方法检索到的结果,并从中创建自定义对象。

编辑:我也可以逐个查询和处理每个ComplexObject,实际上这比检索一堆让我们说的500个元素要慢得多,因为MetaData只需要检索一次而不是每次检索单个对象(有更多的原因,但这个是最方便的)。

1 个答案:

答案 0 :(得分:1)

您应该避免将结果完全存储在列表中。主要思想是实例化每个昂贵的对象,处理它,然后立即处理它。

这意味着你的外部方法看起来像:

foreach (var chunk in IDs.SplitIntoChunks(size: 500))
{
    foreach (var parcel in EnumerateComplexObjects(chunk))
    {
        yield return parcel;
    }
}

EnumerableComplexObjects也会使用yield return

IEnumerable<ComplexObject> EnumerateObjects(IEnumerable<int> ids)
{
    foreach (var id in ids)
    {
        using (var obj = CreateSingleComplexObject(id))
        {
            yield return obj;
        }
    }
}

请注意,一旦你这样做,你甚至不再需要块了:

foreach (var obj in EnumerateObjects(allIDs)
{
     Process(obj);
}