2013-08-30 2 views
3

우리는 우리의 방법 중 일부를 최적화하려고합니다. Redgate의 성능 프로파일 러를 사용하여 성능 누출을 찾습니다.개체 - FirstOrDefault 성능에 C# Linq

Google 도구는 여러 방법으로 개체에 Linq를 사용합니다. 그러나 우리는 FirstOrDefault이 +/- 1000 개체를 가진 콜렉션에 매우 오래 걸리는 것으로 나타났습니다.

또한 프로파일 러는 쿼리가 매우 느리게 진행되었음을 경고합니다. 프로파일 러 결과가있는 이미지를 추가했습니다.

컬렉션을 데이터베이스에 추가 한 다음 데이터베이스를 쿼리 할 수 ​​없습니다. 추천 사항이 있으십니까?

감사합니다.

private SaldoPrivatiefKlantVerdeelsleutel GetParentSaldoPrivatiefKlantVerdeelsleutel(SaldoPrivatiefKlantVerdeelsleutel saldoPrivatiefKlantVerdeelsleutel, SaldoGebouwRekeningBoeking boeking, int privatiefKlant) 
{ 
    SaldoPrivatiefKlantVerdeelsleutel parentSaldoPrivatiefKlantVerdeelsleutel = null; 

    if (saldoPrivatiefKlantVerdeelsleutel != null) 
    { 
     try 
     { 
      parentSaldoPrivatiefKlantVerdeelsleutel = saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection 
       .FirstOrDefault(s => (boeking == null || (s.SaldoVerdeelsleutel != null && 
       (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) 
       && s.PrivatiefKlant.ID == privatiefKlant); 
     } 
     catch (Exception ex) 
     { } 
    } 

    return parentSaldoPrivatiefKlantVerdeelsleutel; 
} 

이미지 : Profile report

+0

내 첫번째 생각은 확인 null' ==은'boeking을 이동하는 것입니다, 그리고 루프 _outside_은'SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID' 값입니다. 컬렉션의 모든 요소에 대해 다시 평가할 필요는 없습니다. –

+1

식에서 읽으면 쿼리가 실행됩니다. 이 경우 FirstOrDefault는 전체 식 트리를 실행하므로 느린 FirstOrDefault는 아닙니다. –

+2

필수적인주의 사항 : 비어있는'catch {}'는 매우 해로울 수 있습니다. –

답변

6

당신은

saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection 
      .Where(s => (boeking == null || (s.SaldoVerdeelsleutel != null && 
       (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) && s.PrivatiefKlant.ID == privatiefKlant) 
      .FirstOrDefault() 

이 빠른 이유에 대한 Why is LINQ .Where(predicate).First() faster than .First(predicate)?를 참조로 재 작성하여 속도를 할 수 있어야한다.

+0

흥미 롭습니다. +1 – stevepkr84

2

FirstOrDefault 소스 컬렉션에서 표준 선형 검색을 수행하고 술어와 일치하는 첫 번째 요소를 반환합니다. 그것은 O (n)이므로 더 큰 컬렉션에서 더 많은 시간이 걸리는 것은 놀라운 일이 아닙니다.

다음을 시도해 볼 수는 있지만, 여전히 이득이 커지지는 않습니다. 왜냐하면 여전히 O (n)이기 때문입니다.

private SaldoPrivatiefKlantVerdeelsleutel GetParentSaldoPrivatiefKlantVerdeelsleutel(SaldoPrivatiefKlantVerdeelsleutel saldoPrivatiefKlantVerdeelsleutel, SaldoGebouwRekeningBoeking boeking, int privatiefKlant) 
{ 
    SaldoPrivatiefKlantVerdeelsleutel parentSaldoPrivatiefKlantVerdeelsleutel = null; 

    if (saldoPrivatiefKlantVerdeelsleutel != null) 
    { 
     try 
     { 
      var query = saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode 
                 .SaldoPrivatiefKlantVerdeelsleutelCollection 
                 .Where(s => s.PrivatiefKlant.ID == privatiefKlant); 

      if(boeking != null) 
      { 
       var gebouwVerdeelSleutelId = boeking.SaldoGebouwRekeningVerdeling 
                .SaldoGebouwRekening 
                .SaldoVerdeelsleutel 
                .GebouwVerdeelSleutel 
                .ID; 

       query = query.Where(s => s.SaldoVerdeelsleutel != null && 
        s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == gebouwVerdeelSleutelId); 
      } 
      parentSaldoPrivatiefKlantVerdeelsleutel = query.FirstOrDefault(); 
     } 
     catch (Exception ex) 
     { } 
    } 

    return parentSaldoPrivatiefKlantVerdeelsleutel; 
} 

boeking != null 확인이되지 모든 소스 콜렉션 요소에 한 번만 수행되기 때문에 그것은 더 할 것입니다. nested Where calls are combined 때문에 성능이 저하되지 않습니다.

2

나는이

boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID 

범인이 될 수 있다고 말할 것이다.

var id = boeking != null ? boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID : 0; 

를 쿼리 내부의 id를 사용처럼 외부를 캐싱보십시오.

은 (내가 상상하고 있어요 : 그 긴 사슬의 특성 중 하나가 "너무 똑똑하지"아주 느린 실제로 뭔가 할) 당신은 간단한 코드처럼 쓸 시도 할 수

0

합니다. LINQ는 위임자를 사용하기 때문에 약간의 성능이 저하됩니다.

   try 
       { 
        parentSaldoPrivatiefKlantVerdeelsleutel = null; 
        foreach (var s in saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection) 
        { 
         if ((boeking == null || (s.SaldoVerdeelsleutel != null && (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) && s.PrivatiefKlant.ID == privatiefKlant) 
         { 
          parentSaldoPrivatiefKlantVerdeelsleutel = s; 
          break; 
         } 
        } 
       } 
       catch (Exception ex) 
       { } 
관련 문제