Как предотвратить переполнение памяти при использовании IEnumerable ‹T› и Linq-To-Sql?

Этот вопрос связан с моим предыдущим вопросом

Это мой текущий код

 IEnumerable<Shape> Get()
 {
     while(//get implementation
         yield return new Shape(//...
 }

 void Insert()
 {
       var actual = Get();
       using (var db = new DataClassesDataContext())
       {
           db.Shapes.InsertAllOnSubmit(actual);
           db.SubmitChanges();
       }
 }

Я получаю переполнение памяти, поскольку IEnumerable слишком велик. Как мне это предотвратить?


person Jader Dias    schedule 23.06.2009    source источник


Ответы (4)


Попробуйте использовать InsertOnSubmit вместо InsertAllOnSubmit. А затем совершайте фиксацию через определенные промежутки времени, как сказал Эрих.

Или, если вы хотите сделать это партиями, например, 5 попробуйте Handcraftsman's или dtb для получения IEnumerable от IEnumerable. Например, с чанком dtb:

   var actual = Get();
   using (var db = new DataClassesDataContext())
   {
       foreach(var batch in actual.Chunk(5))
       {
         db.Shapes.InsertAllOnSubmit(batch);
         db.SubmitChanges();
       }
   }
person Matthew Flaschen    schedule 23.06.2009

Один из вариантов - разбить его на несколько партий. Создайте временный буфер из Shape объектов, выполняйте итерацию, пока вы не заполните его или не выйдете из счетчика, затем выполните InsertBatchOnSubmit.

person Erich Mirabal    schedule 23.06.2009
comment
Как мне собрать все элементы в группы по 5 человек? - person Jader Dias; 23.06.2009
comment
Я понял, что InsertBatchOnSubmit будет InsertAllOnSubmit с меньшим количеством элементов. - person Jader Dias; 23.06.2009
comment
Ссылка Earwicker является прекрасным примером. Я не уверен, что это поможет вам, поскольку вы выполняете отложенное выполнение. Возможно, вам понадобится List ‹Shape› и batchSize = 5 вне цикла. Добавьте элементы из перечислителя, вставьте, когда счетчик достигнет batchSize, а затем очистите предыдущий пакет. Это то, о чем вы спрашивали? - person Erich Mirabal; 23.06.2009

Используйте следующий метод расширения, чтобы разбить ввод на подмножества подходящего размера.

public static class IEnumerableExtensions
{
    public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max)
    {
        List<T> toReturn = new List<T>();
        foreach(var item in source)
        {
            toReturn.Add(item);
            if (toReturn.Count == max)
            {
                yield return toReturn;
                toReturn = new List<T>();
            }
        }
        if (toReturn.Any())
        {
            yield return toReturn;
        }
    }
}

затем сохраните подмножества

void Insert()
{
    var actual = Get();
    using (var db = new DataClassesDataContext())
    {
        foreach (var set in actual.InSetsOf(5))
        {
            db.Shapes.InsertAllOnSubmit(set);
            db.SubmitChanges();
        }
    }
}

Вы также можете найти этот MSDN статья о InsertOnSubmit () и InsertAllOnSubmit () будет полезной.

person Handcraftsman    schedule 23.06.2009
comment
Используйте toReturn.Clear () вместо toReturn = new List, чтобы избежать накладных расходов. Это похоже на stackoverflow.com/questions/1008785/, но более явный. - person Matthew Flaschen; 24.06.2009
comment
Очистка списка вместо создания нового имеет побочный эффект в виде неожиданного изменения результата, который был возвращен ранее, проблема, если он еще не был использован вызывающей стороной. Например: Enumerable.Range (1, 100) .InSetsOf (5) .InSetsOf (5) .ToList (). ForEach (x = ›Console.WriteLine (x.First (). First () + - + x.Last ().Последний())); получает 1-25 26-50 51-75 76-100, как закодировано, но 21-25 46-50 71-75 96-100, если список только очищен. Кроме того, поскольку GroupBy не используется, он может лениво возвращать результаты вместо того, чтобы сначала использовать весь ввод. - person Handcraftsman; 25.06.2009

Чтобы получить изящный способ получать партии элементов из IEnumerable, см. Это:

C #: самый простой способ разделить массив строк на N экземпляров длиной N элементов

Обновление: ничего хорошего, это работает с массивами. Если у меня будет время позже и никто ничего не предоставит, я напишу это ...

person Daniel Earwicker    schedule 23.06.2009
comment
Сработает ли это в его случае? Он не знает размера, так как у него есть только IEnumerable. - person Erich Mirabal; 23.06.2009
comment
Эрик Липперт указал Эриху на решение (stackoverflow.com/questions/1008785#answer-1008855). dtb предоставил функцию, которая принимает IEumearable ‹T› и возвращает IEnumerable ‹IEnumerable ‹T››. Каждый внутренний IEnumerable ‹T› имеет до (например) 5 элементов. - person Matthew Flaschen; 24.06.2009