Pergunta sobre yield, .net, linq-to-sql, ienumerable, c# – Como evitar o estouro de memória ao usar um IEnumerable <T> e Linq-To-Sql?

1

Esta questão está relacionada comuma pergunta anterior minha

Esse é o meu código atual

<code> 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();
       }
 }
</code>

Estou recebendo um estouro de memória, já que o IEnumerable é muito grande. Como evito isso?

Sua resposta

4   a resposta
3

Uma opção é dividi-lo em vários lotes. Crie um buffer temporário deShape objetos, iterar até você preenchê-lo ou acabar com o enumerador, em seguida, fazer umInsertBatchOnSubmit.

Eu entendi que InsertBatchOnSubmit seria um InsertAllOnSubmit com menos elementos Jader Dias
Como eu conseguiria todos os elementos em grupos de 5? Jader Dias
A ligação de Earwicker é um excelente exemplo. Eu não tenho certeza de que irá ajudá-lo desde que você está fazendo a execução adiada, no entanto. Você pode ter que ter uma List <Shape> e batchSize = 5 fora de um loop. Adicione itens do seu enumerador, insira quando a contagem atingir o batchSize e, em seguida, limpe o lote anterior. É isso que você estava perguntando? Erich Mirabal
3

Tente usarInsertOnSubmit ao invés deInsertAllOnSubmit. E depois cometer em intervalos apropriados, como Erich disse.

Ou, se você quiser fazê-lo em lotes de, e. 5, tenteArtesão oudtb's soluções para obter IEnumerable de IEnumerable. Por exemplo, com o Chunk do dtb:

<code>   var actual = Get();
   using (var db = new DataClassesDataContext())
   {
       foreach(var batch in actual.Chunk(5))
       {
         db.Shapes.InsertAllOnSubmit(batch);
         db.SubmitChanges();
       }
   }
</code>
2

Use o seguinte método de extensão para dividir a entrada em subconjuntos de tamanho apropriado

<code>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;
        }
    }
}
</code>

então persistir os subconjuntos

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

Você também pode encontrareste artigo do MSDN em InsertOnSubmit () vs InsertAllOnSubmit () para ser útil.

Use toReturn.Clear () em vez de toReturn = new List para evitar sobrecarga. Isso é semelhante astackoverflow.com/questions/1008785/… mas um pouco mais explícito. Matthew Flaschen
Limpar a lista em vez de criar uma nova tem o efeito colateral de alterar inesperadamente o resultado que foi retornado anteriormente, um problema se ainda não tivesse sido consumido pelo chamador. Por exemplo: Enumerable.Range (1, 100) .InSetsOf (5) .InSetsOf (5) .ToList (). ForEach (x => Console.WriteLine (x.First (). First () + "-" + x .Last (). Último ())); obtém 1-25 26-50 51-75 76-100 como codificado, mas 21-25 46-50 71-75 96-100 se a lista estiver limpa apenas. Além disso, como o GroupBy não é usado, ele pode retornar resultados lentamente em vez de consumir toda a entrada primeiro. Handcraftsman
1

Para obter uma maneira simples de obter lotes de itens de um IEnumerable, veja isto:

C #: maneira mais limpa de dividir uma matriz de cadeia em N instâncias N itens longos

Atualização: não é bom, isso funciona em matrizes. Se eu tiver algum tempo depois e ninguém mais tiver fornecido algo, eu irei escrever ...

@ homem idéia legal! Jader Dias
Isso funcionaria no caso dele? Ele não sabe o tamanho desde que ele só tem um IEnumerable. Erich Mirabal
Eric Lippert apontou Erich para a solução (stackoverflow.com/questions/1008785#answer-1008855). O dtb deu uma função que usa um IEumearable <T> e retorna um IEnumerable <IEnumerable <T >>. Cada um dos IEnumerable <T> internos tem até (por exemplo) 5 elementos. Matthew Flaschen
Veja a parte 'Update' da minha resposta! Estou cozinhando o jantar agora ... Daniel Earwicker

Perguntas relacionadas