In letzter Zeit kommt es öfter vor, dass große Datenmengen durchiteriert werden müssen. Dabei kommt man manchmal an die Grenzen des Möglichen. Gerade bei wenig RAM kann es passieren, dass dieser bei großen Datenmengen nicht ausreicht und man in eine OutOfMemoryException läuft.
Mit diesem Artikel starten wir eine kleine Artikelserie in unserem Blog. Wir nennen diese „Hilfreiche Funktionen in C# .NET“. Wir werden hier nach und nach kleine Hilfsfunktionen vorstellen, welche wir geschrieben haben, um uns das Leben ein wenig einfacher zu machen.
Das kann man durch das sogenannte „Chunking“ verhindern. Chunking bedeutet, dass man nicht alle Datensätze in den RAM lädt und iteriert, sondern die Datenmenge in kleinere Teilstücke aufteilt und iteriert.
Hierfür habe ich mir die folgende Funktion geschrieben:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/// <summary> /// performs a query to entity framework and returns /// the return value of EF as small chunks one by one. /// </summary> /// <param name="q">query object (the object on what this function was used)</param> /// <param name="size">chunk size</param> /// <typeparam name="T">type of query object</typeparam> /// <returns>every single chunk of specified size, one by one</returns> public static IEnumerable<T[]> QueryChunksOfSize<T>(this IOrderedQueryable<T> q, int size) { var chunkNum = 0; while (true) { // if first chunk, use complete query, if not, skip already returned chunks var query = (chunkNum == 0) ? q : q.Skip(chunkNum * size); var chunk = query.Take(size).ToArray(); if (chunk.Length == 0) { yield break; // finished - all chunks are returned } yield return chunk; // yield return will return every chunk by itself chunkNum++; } } |
Ich habe die Funktion in eine Hilfsklasse gelegt, in welcher schon einige weitere Hilfsfunktionen wie diese gehalten werden. Da die Funktion statisch ist und als ersten Parameter ein Objekt mit this übergeben bekommt, kann man sie direkt als Erweiterungsfunktion auf ein Listobjekt oder, wie es wahrscheinlich am meisten Sinn macht, an einem Entityobjekt benutzen.
Durch das yield return kommen dann immer Teilstücke in der als „size“ übergebenen Größe zurück. Diese kann man dann wie gewohnt iterieren, ohne dass man Speicherprobleme bekommt. Man benutzt lediglich eine weitere Schleife.
Ein Kommentar