2016-08-18 5 views
1

시나리오 나는 데이터베이스를 통해 500 만 항목을 읽고 메모리에 컬렉션의 모든 저장하지 않고 하나씩 처리해야쿼리 결과를 메모리에 저장하지 않고 리포지토리를 구현하는 방법은 무엇입니까?

. (질문하여 LINQ, 그룹의 용도에 관한주의 등을 계산하십시오) 나를 명확히하기 위해 과도하게 단순화 C#을 영감 의사를 쓰자 -

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에를 사용하는 경우, 내가 돈 ' 메모리에 5 백만 개의 항목을 모두 저장해야합니다. 나는 그것들을 반복 할 수 있고 나의 메모리 요구 사항은 한 번에 한 행에 불과하다.

그러나 IEnumerable 등으로 저장소 패턴을 사용하면 처리하기 전에 메모리에 5 백만 개의 항목을 모두 저장해야합니다.

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

내가 저장소 패턴을 건너 뛰고이 옛날 방식대로해야 - 코드는 같을 것이다? 아니면 메모리에 모든 것을로드하지 않고 리포지토리 패턴의 이점을 얻는 방법이 있습니까?

참고 : 내 마음이 repository.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 구현 사용하여 구현 :

,369을
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)) result.Add (person.Name);"때문에 주로 피하고있다. 일부는 나에게 비즈니스 로직의 비트처럼 느껴진다. (비록 그것이 사실상 where 절과 유사하다는 것을 알지만). IDataReader 팩토리를 주입하는 것이 무슨 뜻인지 자세히 설명해 주시겠습니까? IDataReader를 비즈니스 계층에 표시하겠습니까? – Achilles

+0

@Achilles 완벽 주의자가 되서는 안됩니다.) 이제 어떤 논리도 즉각 도메인 * (아이러니 모드 꺼짐)이됩니다. 저장소는 메모리 내 모음처럼 작동하며 도메인과 데이터 매퍼 계층 사이의 장벽입니다. 아무도 당신이 저장소 방법 내에서'if' 또는 더 복잡한 로직을 사용할 수 없다고 말하지는 않습니다. 다른 계층이 그렇게 효율적으로 수행 할 수없는 경우 저장소 내의 개체 필터링은 정상입니다. –

+0

@Achilles 공장에 관해서는, 성 윈저 (Windsor)와 같은 컨트롤 컨테이너의 인 버젼 주입과 반전을 사용하여 수동으로 연결과 판독기를 열기보다는 IDataReader 구현을 주입하는 것을 의미합니다. 이렇게하면 repo 테스트 가능성이 높아집니다. –