2016-08-18 2 views
1

СценарийКак реализовать репозиторий без сохранения результатов запроса в памяти?

мне нужно прочитать более 5 миллионов элементов из базы данных и обрабатывать их один за другим, без необходимости хранить все коллекции в памяти. Позволь мне писать упрощенно C# вдохновили псевдокод для уточнения (обратите внимание, что речь идет о использованиях LINQ, групп по и сосчитать и т.д.) -

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); 

Проблема Если я использую IDataReader, я не» t должен хранить все 5 миллионов элементов в памяти. Я могу просто зациклиться на них, и моя потребность в памяти - всего одна строка за раз.

Но если я использую шаблон репозитория с IEnumerable и т. Д., Тогда я буду вынужден хранить все 5 миллионов элементов в памяти, прежде чем я смогу их обработать. Код будет выглядеть так:

IEnumerable<...> tableData = repository.GetAll() // Here we loaded everything in the memory 
foreach(var row in tableData) 
    //Do whatever... 

Должен ли я пропустить шаблон хранилища и сделать это по-старому? Или есть способ получить преимущества шаблона Репозитория, не загружая все в память?

Примечание: Решение, которое приходит мне на ум, создает репозиторий .GetAggregatedResult (Func aggregateFunction) , но это не чувствует себя чище. Кроме того, реальная проблема здесь - как перебираются на хранилище одного пункта в то время, не сохраняя весь набор результатов в памяти

+0

Вы всегда можете написать свой репозиторий, чтобы принять параметры и выбрать только определенные строки или определенное количество строк. – Luke

+0

Справа. Но мы хотим прочитать все строки. Просто мы хотим обработать каждую строку, как только она будет прочитана, а затем отбросьте вместо сохранения в памяти. – Achilles

+0

Вы не можете ожидать наличия списка строк и не хранить их в памяти, потому что, если у вас есть список _something_, он находится в памяти. Вы должны разумно загружать их, обрабатывать их, а затем загружать еще несколько. Что случилось с установкой соединения, а затем с использованием того же соединения для запроса большего количества строк? – Luke

ответ

1

Я не понимаю, почему вы не можете реализовать метод, как это:

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

Кроме того, объект домена следующим образом:

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 ;) 
} 

... и реализовать его с помощью сырой IDataReader реализации:

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

Если вы хотите сделать его абсолютно агностиком, вы можете использовать инъекцию зависимостей в репозитории, чтобы ввести завод IDataReader!

Теперь вы можете продолжать с миром чудес шаблон репозитория:

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

Спасибо, мне нравится этот подход до сих пор. Я очень склонен использовать его, но я избегал его главным образом потому, что результат «if (predicate (person)) .Add (person.Name);» часть чувствует себя немного логической логикой для меня (хотя я понимаю, что она действительно похожа на предложение where). Не могли бы вы рассказать о том, что вы подразумеваете, вводя фабрику IDataReader? Вы имели в виду - вывести IDataReader на бизнес-уровень? – Achilles

+0

@Achilles Не быть перфекционистом;) Теперь любая логика мгновенно будет * domain * (иронический режим выключен). Репозиторий действует как коллекция в памяти, и это барьер между доменом и уровнем отображения данных. Никто не говорит, что вы не можете использовать 'if' или даже более сложную логику в методе репозитория. Это нормально фильтровать объекты в репозитории, если другой слой не сможет сделать это эффективно. –

+0

@Achilles О фабрике, я имею в виду использование инъекции зависимостей и инверсию управляющего контейнера, такого как Castle Windsor, для внедрения реализации IDataReader вместо того, чтобы вручную открывать там соединение и читателя. Это увеличит вашу тестовую способность репо. –