2013-04-26 6 views
5

Projektuję wyniki LINQ do SQL na silnie typowane klasy: Parent i Child. Różnica wydajności pomiędzy tymi dwoma zapytaniami jest duża:Odwzorowane zapytanie LINQ typu jeden-do-wielu wykonuje się wielokrotnie

Powolne Query - zalogowaniu z DataContext pokazuje, że oddzielne wywołanie db jest dokonywana dla każdego rodzica

var q = from p in parenttable 
     select new Parent() 
     { 
      id = p.id, 
      Children = (from c in childtable 
         where c.parentid = p.id 
         select c).ToList() 
     } 
return q.ToList() //SLOW 

Szybka kwerenda - zalogowaniu z pokazów DataContext jedno trafienie db kwerendy, która zwraca wszystkie wymagane dane

var q = from p in parenttable 
     select new Parent() 
     { 
      id = p.id, 
      Children = from c in childtable 
         where c.parentid = p.id 
         select c 
     } 
return q.ToList() //FAST 

Chcę zmusić LINQ użyć stylu pojedynczych zapytań drugiego przykładu, ale zapełnić klasy Parent ze swoimi dziećmi obiektów bezpośrednio. w przeciwnym razie właściwość dla dzieci to IQuerierable<Child>, która musi zostać sprawdzona w celu odsłonięcia obiektu podrzędnego.

Odniesione pytania nie odnoszą się do mojej sytuacji. korzystanie z db.LoadOptions nie działa. być może wymaga to, aby typ był zarejestrowany w DataContext.

DataLoadOptions options = new DataLoadOptions(); 
    options.LoadWith<Parent>(p => p.Children); 
    db.LoadOptions = options; 

Uwaga: Rodzic i dziecko są typy proste, nie Table<TEntity> typów. i nie ma relacji kontekstowej między rodzicem a dzieckiem. podzapytania są ad-hoc.

Sedno problemu: w drugim przykładzie LINQ implementuję instrukcje IQueriable i nie wywołuję funkcji ToList() iz jakiegoś powodu LINQ wie, jak wygenerować jedno zapytanie, które może pobrać wszystkie wymagane dane. W jaki sposób zapełnić moją projekcję ad-hoc rzeczywistymi danymi, jak ma to miejsce w pierwszym zapytaniu? Ponadto, jeśli ktokolwiek mógłby mi pomóc lepiej - wyrażenie mojego pytania, byłbym wdzięczny.

+0

Jak "dziecko" może być "prostym typem"? Powinien to być typ odwzorowany. I jest "rodzicielskie" a "Tabela" lub wynik poprzedniego zapytania linq? Niektóre instrukcje linq mogą spowodować, że L2S przełączy się z łączenia na N + 1. –

+1

Zduplikowane markery: czy znasz różnicę między strukturą encji a linq do sql? –

+0

@GertArnold Podczas gdy część LINQ EF/L2S często sprawia, że ​​podobne pytania funkcjonalnie duplikują, w * tym * przypadku uważam, że jesteś całkowicie poprawny; nie są duplikatami. Ponowne otwieranie. –

Odpowiedz

0

Należy ustawić poprawne opcje ładowania danych.

options.LoadWith<Document>(d => d.Metadata); 

Spójrz na this

PS: Include tylko dla numeru .

+0

Wygląda na to, że powinna być odpowiedzią, ale nie zadziałało to dla mnie. Próbowałem dlo.LoadWith (p => p.Children). nadal ma wiele trafień db – Paul

+0

@Paul Nie musisz używać parenttable. Czy wygenerowałeś obiekt Parent przez LINQ2SQL? Wtedy twoja encja zawiera Dzieci już wtedy, gdy poprosisz Rodzica z kontekstu. –

0

Drugie zapytanie jest szybkie właśnie dlatego, że Dzieci nie są wypełniane.

Pierwsza z nich jest powolna tylko dlatego, że są zamieszkiwane dzieci.

Wybierz ten, który najlepiej pasuje do Twoich potrzeb, po prostu nie możesz mieć swoich funkcji razem!

EDIT:

Jak @Servy mówi:

W swoim drugim zapytaniu nie są faktycznie pobierania żadnych informacji o dzieci. Utworzono zapytania, ale w rzeczywistości nie wykonano ich, aby uzyskać wyniki tych zapytań. Jeśli chcesz powtórzyć listę, a następnie powtórzyć kolekcję Dzieci każdego przedmiotu, który zobaczysz, biorąc pod uwagę czas, jaki jest pierwszym zapytaniem.

1

Najszybszym sposobem na wykonanie tego zadania jest wykonanie zapytania, które zwróci wszystkie wyniki, a następnie zgrupuje wszystkie wyniki. Upewnij się, że zrobisz .ToList() na pierwszym zapytaniu, tak, że drugie zapytanie nie wykonuje wielu połączeń.

Tutaj powinno się znaleźć to, co chcesz osiągnąć za pomocą tylko jednego zapytania db.

  var q = from p in parenttable 
        join c in childtable on p.id equals c.parentid 
        select c).ToList(); 
      var r = q.GroupBy(x => x.parentid).Select(x => new { id = x.Key, Children=x }); 
+1

Zamiast łączenia, a następnie grupy, wystarczy użyć GroupJoin. – Servy

5

Ważne jest, aby pamiętać, że zapytań LINQ polegać w odroczonego wykonanie. W drugim zapytaniu nie otrzymujesz żadnych informacji o dzieciach. Utworzono zapytania, ale w rzeczywistości nie wykonano ich, aby uzyskać wyniki tych zapytań. Jeśli chcesz powtórzyć listę, a następnie wykonaj iterację kolekcji Children każdego elementu, który zobaczysz, biorąc pod uwagę czas, jaki upłynął od pierwszego zapytania.

Twoje zapytanie jest również z natury bardzo nieefektywne. Używasz zagnieżdżonego zapytania, aby przedstawić relację Join. Jeśli użyjesz Join, zapytanie będzie mogło zostać zoptymalizowane odpowiednio zarówno przez dostawcę zapytań, jak i bazę danych, aby wykonać znacznie szybciej. Konieczne może być również dostosowanie indeksów w bazie danych w celu poprawy wydajności. Oto, jak może wyglądać łączenie:

var q = from p in parenttable 
     join child in childtable 
     on p.id equals child.parentid into children 
     select new Parent() 
     { 
      id = p.id, 
      Children = children.ToList(), 
     } 
return q.ToList() //SLOW 
+0

Wiem, że pytanie dotyczy LINQ do SQL, ale myślałem, że wspomnę, że jeśli używasz Entity Framework, 'ToList()' nie jest wymagane na 'children'. –

Powiązane problemy