2011-04-12 1 views
5

C# (LINQ, IEnumerable, IQueryable, ...)의 지연 평가에 크게 의존하는 응용 프로그램을 디자인하는 일반적인 방법은 무엇입니까?C# laziness 질문

지금은 일반적으로 yield returnLINQ 쿼리를 사용하여 가능한 한 모든 쿼리가 같은 게으른을 시도하지만, 모든 쿼리 결과 분명히 시작에 그것에서 builts을 얻을 때 런타임이 보통 "너무 게으른" 동작이 발생할 수있다 심각한 시각적 성능 저하가 발생합니다.

보통 내가하는 일은 ToList() 투영 연산자를 어딘가에 배치하여 데이터를 캐시하는 것을 의미하지만,이 방법이 올바르지 않을 수도 있습니다.

처음부터 이러한 종류의 응용 프로그램을 설계하는 데 적절한/일반적인 방법은 무엇입니까?

답변

4

각 IEnumerable을 세 가지 범주 중 하나로 분류하는 것이 유용하다는 것을 알았습니다.

  1. 빠른 항목 - 예 : 목록 및 배열
  2. 느린 것 - 예 :데이터베이스 쿼리 또는 과중한 계산
  3. 비 결정적 쿼리 - 예 : list.Select (X => 새로운 {...})

카테고리 1의 경우, I는 적절한 배열 또는 카테고리 3은 IList 등 은 해당 지역 내를 유지하는 것이 가장 때 콘크리트 형 유지 경향 한 가지 방법은 버그를 찾기가 어렵지 않습니다. 그런 다음 카테고리 2가 있으며 성능을 최적화 할 때 항상 병목 현상을 찾아서 측정합니다.

2

몇 임의의 생각은 - 질문 자체가 느슨하게 정의된다

  • 게으른는 결과가 따라서 사용 필요할 때만로드되지 않을 경우에만 좋다. 그러나 대부분의 작업에는로드 할 데이터가 필요하므로 그 용어에서 게으름이 좋지 않습니다.
  • 게으름은 어려운 버그의 원인이 될 수 있습니다. 우리는 그것이 집계 연산자 중 하나 (ToList, ToArray 전화 당신이 캐시 할 데이터의 특정 순서를 필요 MEF
1

에 올 때

  • 게으른 좋은으로 ORMs의 데이터 컨텍스트에 모든 것을 보았다 등). 그렇지 않으면 lazy evaluation만을 사용하십시오.

    데이터 주위에 코드를 작성하십시오. 휘발성이고 매번 신선한 데이터를 가져와야하는 데이터는 무엇입니까? lazy evaluation을 사용하고 캐시하지 마십시오. 어떤 데이터가 비교적 정적이며 한번만 끌어 와야합니까? 데이터를 메모리에 캐시하여 불필요하게 가져 오지 않도록하십시오.

  • 2

    매우 광범위한 질문과 불행히도 당신은이 말을 많이 듣게 될 것입니다. 게으른 로딩은 그렇지 않을 때까지 훌륭합니다.

    일반적으로 동일한 IEnumerables를 반복해서 사용하는 경우 목록으로 캐시하는 것이 가장 좋습니다.

    하지만 발신자가이 사실을 알지 못하는 경우는 거의 없습니다. 즉, IEnumerables를 리포지토리 등에서 가져 오는 경우 리포지토리에서 작업을 수행하는 것이 가장 좋습니다. 내부적으로 목록으로 캐시 할 수도 있고 매번 빌드 할 수도 있습니다. 발신자가 너무 영리하려고하면 그들은 등, 데이터의 변화를 놓칠 수

    2
    난 당신이 위의 예에서 DTO

    public IList<UserDTO> GetUsers() 
    { 
        using (var db = new DbContext()) 
        { 
        return (from u in db.tblUsers 
          select new UserDTO() 
          { 
           Name = u.Name 
          }).ToList(); 
        } 
    } 
    

    을 반환하기 전에 DAL에서 ToList을하고 제안

    는이 DbContext 범위가 끝나기 전에 ToList()를 수행해야합니다.

    +1

    신성한 몰리! 나는 이것과 거의 비슷한 코드 톤을 가지고있다. :) –

    0

    .ToList()으로 모든 항목을 지연 실행하고 캐싱하는 것이 유일한 옵션은 아닙니다. 세 번째 옵션은 지연된 목록을 사용하여 을 캐시하고을 반복하는 것입니다.

    실행은 여전히 ​​연기되지만 모든 항목은 한 번만 생성됩니다. 어떻게이 작품의 예 :

    public class LazyListTest 
    { 
        private int _count = 0; 
    
        public void Test() 
        { 
         var numbers = Enumerable.Range(1, 40); 
         var numbersQuery = numbers.Select(GetElement).ToLazyList(); // Cache lazy 
         var total = numbersQuery.Take(3) 
          .Concat(numbersQuery.Take(10)) 
          .Concat(numbersQuery.Take(3)) 
          .Sum(); 
         Console.WriteLine(_count); 
        } 
    
        private int GetElement(int value) 
        { 
         _count++; 
         // Some slow stuff here... 
         return value * 100; 
        } 
    } 
    

    당신이 테스트() 메소드를 실행하면 _count 만 (10)가 16로 될 것이다 캐싱하지 않고있다. ToList() 그것은 40 일 것입니다!

    implementation of LazyList can be found here의 예입니다.