entity .ToList () генерирует исключение System.OutOfMemoryException

У меня есть таблица с полмиллионом строк. Мне нужно обновить каждую строку, но ToList () не работает:

List<Contacts> allContacts = objDatabase.Contacts.ToList();

Каждый раз я получаю System.OutOfMemoryException. Это можно обойти?

У меня уже есть обходной путь App.Config, но все еще нет:

<gcAllowVeryLargeObjects enabled="true" />    

Я на 64-битной машине с 8 ГБ ОЗУ


person Community    schedule 17.09.2014    source источник
comment
Зачем вам нужны все эти 500 000 записей сразу? Попробуйте собрать их по частям, обработать и получить еще один кусок.   -  person brz    schedule 18.09.2014
comment
Вы пробовали перечислять элементы один за другим, используя цикл foreach, чтобы передавать их один за другим из базы данных? В любом случае, 500 000 элементов - это довольно много, чтобы справиться за один раз при использовании ORM.   -  person Martin Costello    schedule 18.09.2014
comment
Даже если вы на 64-битной машине, вы можете работать в 32-битном процессе. В некоторых шаблонах проектов целевой платформой по умолчанию является x86 или включен параметр «Предпочитать 32-разрядный». Если вы выполняете 62-битный процесс, исчерпание физической памяти не вызывает исключения нехватки памяти; память выгружается на диск, что обычно приводит к ужасной производительности.   -  person phoog    schedule 18.09.2014
comment
Возьмите список по частям, как предложил @brz. Используйте Skip () и Take (), и после каждого захвата не забудьте удалить и создать новый контейнер сущностей, чтобы ваша память не использовалась.   -  person Nathan A    schedule 18.09.2014


Ответы (3)


Вот решение с использованием фрагментов. Он будет удалять контейнер (и загруженные объекты) после каждого фрагмента. Сборщик мусора должен освободить память задолго до того, как в вашей системе закончится память.

int chunkSize = 50;
int curCount = 0;

while (true)
{
    using (var db = new DbEntities())
    {
        var chunk = db.Contacts.Skip(curCount).Take(chunkSize).ToArray();
        curCount += chunkSize;

        if (chunk.Length == 0) break;

        foreach (var contact in chunk)
        {
            //do any work for the contact here
            contact.Something = "SomethingNew";
        }

        db.SaveChanges();
    }
}

Не стесняйтесь экспериментировать с размером блока. Чем больше фрагмент, тем быстрее должен быть весь процесс, но он будет использовать больше памяти.

person Nathan A    schedule 17.09.2014
comment
Спасибо, я думаю, это единственный выход. Довольно странно, что .NET не может обрабатывать полмиллиона строк, это 2014 год. - person ; 18.09.2014
comment
Пришлось добавить заказ перед вызовом skip. кроме этого он работает отлично! - person Nathan Prather; 14.03.2020

Как насчет

IEnumerable<Contacts> allContacts = objDatabase.Contacts.AsEnumerable();

Никогда не преобразовывайте все контакты в список. Просто используйте перечислитель и примените цикл Foreach для доступа к каждому контакту.

person Yogee    schedule 17.09.2014
comment
Это хорошо, но теперь такое же исключение возникает в SaveChanges (), я думаю, мне придется разделить обработку на фрагменты, как и другие предложенные - person ; 18.09.2014
comment
См. msdn. microsoft.com/en-us/library/ - person ; 18.09.2014
comment
Ok. получил это сейчас. Мне не нравятся короткие решения, но, похоже, пока это единственный способ. - person Yogee; 18.09.2014

пытаться

foreach (Contacts c in objDatabase.Contacts) c.value = newvalue;
person paparazzo    schedule 17.09.2014
comment
Это все равно приведет к загрузке всех возможных записей в локальную память. - person Nathan A; 18.09.2014
comment
Натан. BS. Это по одной записи за раз. Так работает IEnumerable. - person paparazzo; 18.09.2014
comment
Вы исследуете. После начала foreach из БД извлекается ВЕСЬ набор результатов: stackoverflow.com/a/18216387/2465182. Это приведет к использованию такой же памяти, как и ToList (). - person Nathan A; 18.09.2014
comment
@NathanA Double BS Вы читали этот ответ? Он использует DataReader, который извлекает результаты по одному (и удерживает соединение). Это не то же самое, что foreash не начинается до тех пор, пока ВЕСЬ набор результатов не будет извлечен из БД. Вы потребляете их по одному, и они выходят из памяти. - person paparazzo; 18.09.2014
comment
Исключение теперь возникает в SaveChanges (), пора разделить обработку на части ... - person ; 18.09.2014
comment
Почему бы не использовать устройство чтения данных и команду? Еще лучше использовать одну команду обновления. - person paparazzo; 18.09.2014