2016-08-18 3 views
1

SzenarioWie Implementieren Repository, ohne Abfrageergebnisse im Arbeitsspeicher zu speichern?

Ich brauche mehr als 5 Millionen Artikel aus der Datenbank zu lesen und sie eins nach dem anderen, ohne speichern alle der Sammlung im Speicher zu verarbeiten. Lassen Sie mich einen vereinfachend C# inspiriert Pseudo-Code schreiben zu klären (bitte beachten Sie, dass die Frage über die Verwendungen von LINQ ist, Gruppe und zählen etc.) -

Lets say the table has the following fields - Id, Name, Age 

IList<string> resultList = ... 
IDataReader reader = command.executereader... 
while(reader.Read()) //Read only one item at a time, no need to load everything 
    if (AggregateFunction(resultList, reader.Name, reader.Age)) 
     resultList.Add(reader.Name); 

Problem Wenn ich einen IDataReader verwenden, I don‘ Ich muss alle 5 Millionen Elemente im Speicher speichern. Ich kann einfach über sie hinwegschleifen und meine Speicheranforderung ist nur eine Reihe zu einer Zeit.

Aber wenn ich Repository-Muster mit IEnumerable etc. verwende, dann werde ich gezwungen sein, alle 5 Millionen Elemente im Speicher zu speichern, bevor ich sie verarbeiten kann. Der Code würde wie aussehen -

Soll ich Repository-Muster überspringen und tun dies nur auf die altmodische Art und Weise? Oder gibt es eine Möglichkeit, die Vorteile des Repository-Musters zu nutzen, ohne alles im Speicher zu laden?

Hinweis: Die Lösung, die mir in den Sinn kommt, ist ein Repository .GetAggregatedResult (Func AggregatFunction) , aber das fühlt sich nicht sauberer. Plus, hier ist das eigentliche Problem - wie auf Repository ein Element in einer Zeit zu durchlaufen, ohne das gesamte Ergebnis im Speicher gesetzt Speicherung

+0

Sie können jederzeit Ihr Repository schreiben Parameter zu akzeptieren und nur bestimmte Zeilen oder bestimmte Anzahl von Zeilen auszuwählen. – Luke

+0

Rechts. Aber wir wollen alle Zeilen lesen. Es ist nur so, dass wir jede Zeile verarbeiten wollen, sobald sie gelesen und dann gelöscht wird, anstatt sie im Speicher zu speichern. – Achilles

+0

Sie können nicht erwarten, eine Liste von Zeilen zu haben und sie nicht im Speicher zu speichern, denn wenn Sie eine Liste von _etwas_ haben, ist es im Speicher. Sie müssen sie intelligent laden, verarbeiten und anschließend laden. Was ist beim Herstellen einer Verbindung falsch, und verwenden Sie dann dieselbe Verbindung, um weitere Zeilen anzufordern? – Luke

Antwort

1

Ich sehe nicht, warum Sie nicht eine Methode wie folgt implementieren:

public interface IPersonRepository 
{ 
    IEnumerable<string> GetFilteredNames(Func<Person, bool> predicate); 
} 

auch ein Domain-Objekt wie folgt:

public class Person 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public byte Age { get; set; } 
    // byte should be fine unless you would be 
    // working with turtles instead of persons ;) 
} 

... und implementieren sie eine rohe IDataReader Implementierung mit:

public IEnumerable<string> GetFilteredNames(Func<Person, bool> predicate) 
{ 
    List<string> result = new List<string>(); 
    IDataReader dataReader = ... // Who knows how you get it! 

    while(dataReader.Read()) 
    { 
     Person person = new Person 
     { 
      Id = (int)dataReader["Id"], 
      Name = (string)dataReader["Name"], 
      Age = (byte)dataReader["Age"] 
     }; 

     if(predicate(person)) 
      result.Add(person.Name); 
    } 

    return result;  
} 

Wenn Sie es absolut agnostisch machen wollen, können Sie möglicherweise Dependency Injection auf Repository verwenden, um eine IDataReader Fabrik zu injizieren!

Jetzt können Sie mit der Welt der Wunder von Repository-Muster weiter:

var result = repoImpl.GetFilteredNames(person => AggregateFunction(person.Id, person.Name, person.Age)); 
+0

Danke, ich mag diesen Ansatz bisher am besten. Ich bin sehr geneigt, es zu benutzen, aber ich habe es vor allem deshalb vermieden, weil das "if (Prädikat (Person)) result.Add (person.Name);" Teil fühlt sich für mich wie ein bisschen Geschäftslogik an (obwohl ich realisiere, dass es einer Where-Klausel sehr ähnlich ist). Könnten Sie bitte erläutern, was Sie meinen, indem Sie eine IDataReader-Fabrik einspeisen? Meinten Sie - den IDataReader der Business-Schicht aussetzen? – Achilles

+0

@Achilles Sei nicht perfektionistisch;) Jetzt wird jede Logik sofort * Domäne * (ironischer Modus aus). Ein Repository verhält sich wie eine In-Memory-Sammlung und ist die Barriere zwischen der Domäne und der Data-Mapper-Schicht. Niemand sagt, dass Sie keine "if" oder noch komplexere Logik innerhalb einer Repository-Methode verwenden können. Es ist in Ordnung, Objekte im Repository zu filtern, wenn andere Ebenen dies nicht effizient tun könnten. –

+0

@Achilles Über die Fabrik, meine ich mit Abhängigkeitsinjektion und einer Inversion von Steuercontainer wie Castle Windsor, um die 'IDataReader'-Implementierung zu injizieren, anstatt dort manuell eine Verbindung und ein Lesegerät zu öffnen. Dies erhöht Ihre Repo-Testbarkeit. –

Verwandte Themen