2009-07-06 12 views
21

Czułem, że następujące rzeczy powinny być możliwe Nie jestem po prostu pewien, jakie podejście zastosować.warunkowe zawierają w linq do encji?

Co chcę zrobić, to użyć metody include, aby ukształtować wyniki, tj. Określić, jak daleko wzdłuż wykresu obiektu przejść. ale ... Chciałbym, żeby to przejście było warunkowe.

something like... 

dealerships 
    .include(d => d.parts.where(p => p.price < 100.00)) 
    .include(d => d.parts.suppliers.where(s => s.country == "brazil")); 

Rozumiem, że to nie jest ważne LINQ, w rzeczywistości, że jest bardzo źle, ale zasadniczo szukam jakiś sposób do zbudowania drzewa wyrażenie, które będzie zwracać kształcie wyniki, co odpowiada ...

select * 
from dealerships as d 
outer join parts as p on d.dealerid = p.dealerid 
    and p.price < 100.00 
outer join suppliers as s on p.partid = s.partid 
    and s.country = 'brazil' 

z naciskiem na warunki łączenia.

Czuję, że byłoby to dość proste z esql, ale moją preferencją byłoby budować drzewa ekspresji w locie.

jak zawsze, wdzięczny za wszelkie rady i wskazówek

+1

Czy kiedykolwiek znalazłeś rozwiązanie tego problemu? Mam ten sam problem. –

+0

Jeszcze nie, ale nadal jestem zainteresowany i będę kontynuował ... – tim

+0

Szukałem tego samego, –

Odpowiedz

15

To powinno załatwić sprawę:

using (TestEntities db = new TestEntities()) 
{ 
    var query = from d in db.Dealership 
       select new 
       { 
        Dealer = d, 
        Parts = d.Part.Where 
        (
         p => p.Price < 100.0 
          && p.Supplier.Country == "Brazil" 
        ), 
        Suppliers = d.Part.Select(p => p.Supplier) 
       }; 

    var dealers = query.ToArray().Select(o => o.Dealer); 
    foreach (var dealer in dealers) 
    { 
     Console.WriteLine(dealer.Name); 
     foreach (var part in dealer.Part) 
     { 
      Console.WriteLine(" " + part.PartId + ", " + part.Price); 
      Console.WriteLine 
       (
       " " 
       + part.Supplier.Name 
       + ", " 
       + part.Supplier.Country 
       ); 
     } 
    } 
} 

Ten kod daje listę dealerów każdy zawierający przefiltrowaną listę części. Każda część odnosi się do Dostawcy. Ciekawe jest to, że musisz stworzyć anonimowe typy w selekcji w pokazany sposób. W przeciwnym razie właściwość Part obiektów dealerskich będzie pusta.

Należy również wykonać instrukcję SQL przed wyborem dealerów z zapytania. W przeciwnym razie właściwość części dealerów znów będzie pusta.Dlatego kładę ToArray() połączenia w następujący wiersz:

var dealers = query.ToArray().Select(o => o.Dealer); 

Ale zgadzam się z Darrenem, że to nie może być to, co użytkownicy biblioteki oczekują.

+2

Uwaga na ten temat, próbowałem tego i początkowo nie działało. W końcu stwierdziłem, że mam włączony Lazy Loading, który powodował, że to nie działa. Gdy wyłączyłem leniwy ładowanie, działało to jak czar. Dzięki Jakob! – msmucker0527

+5

Wymaga to zwrócenia typu anonimowego zamiast typu surowego z faktycznymi odniesieniami. To "działa", ale pozostawia mnie szukającego rozwiązania zwracającego surowy obiekt z faktycznym obiektem kolekcji odfiltrowanym do tego, co faktycznie chciałbym zwrócić. Anonimowy typ zwrotu działa pewnie ... ale tak naprawdę nie jest naprawdę pożądanym rozwiązaniem. – VulgarBinary

+0

@ VulgarBinary można zwrócić listę wpisaną w następujący sposób: 'var dealerów = query.ToArray(). Wybierz (o => o.Dealer) .ToList();' To po prostu działa! –

1

Am I czegoś brakuje, lub są po prostu nie szukając słów kluczowych Any?

var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) || 
           d.parts.suppliers.Any(s => s.country == "brazil")); 
+2

Nie sądzę, ponieważ rozumiem to, to zwróciło dealerów gdzie były części i dostawców, którzy spotkali się z warunkach, zamiast zwracać cały dealership, wszystkie części, które są poniżej 100 i wszystkich dostawców w Brazylii. Zdaję sobie sprawę, że powinno to być sprzężenie zewnętrzne. Myślę, że próbuję zrobić to przycinać drzewa wyników, a nie filtrować węzły najwyższego poziomu na podstawie wartości w drzewie. – tim

6

Czy jesteś pewien, że tego właśnie chcesz? Jedynym powodem, dla którego pytam, jest to, że po dodaniu filtru do Części od Dealerships, twoje wyniki nie są już Dealerships. Masz do czynienia z przedmiotami specjalnymi, które w większości są bardzo zbliżone do salonów sprzedaży (z tymi samymi właściwościami), ale znaczenie własności "Części" jest inne. Zamiast być relacją między salonami a częściami, jest to przefiltrowana relacja.

Albo inaczej mówiąc, jeśli ciągnąć dealerskiej z wynikami i przekazywane do metody napisałem, a następnie w moim metoda zadzwonić:

var count = dealership.Parts.Count(); 

Czekam, aby uzyskać części, a nie odfiltrowane części z Brazylii, których cena jest niższa niż 100 USD.

Jeśli nie użyjesz obiektu dealerskiego do przekazania przefiltrowanych danych, stanie się bardzo łatwy. Staje się to tak proste, jak:

var query = from d in dealerships 
       select new { DealershipName = d.Name, 
CheapBrazilProducts = dealership.Parts.Where(d => d.parts.Any(p => p.price < 100.00) || d.parts.suppliers.Any(s => s.country == "brazil")) }; 

Gdybym tylko miał dostać przefiltrowanych zestawów jak pytasz, to bym pewnie zastosować technikę wspomniano powyżej, a następnie użyj narzędzia jak Automapper skopiować przefiltrowane wyniki moja anonimowa klasa do prawdziwej klasy. Nie jest niewiarygodnie elegancka, ale powinna działać.

Mam nadzieję, że to pomoże! To był interesujący problem.

+0

Dzięki Darren Zgadzam się w zakresie identyczności obiektu! Obawiam się również, że właśnie dlatego EF może nie wspierać tego. Co ja naprawdę próbuje zrobić tutaj jest analizować ciąg kwerendy, takie jak http://carpartsdb.com/dealerships/parts(price <100)/dostawcy (s.country == Brazylia) do wyrażenie zapytania i sprawdź to w EF, chuck w niestandardowej strategii serializacji xml i presto masz silnik RestQL (spokojny język zapytań). Nie jestem zbyt przywiązany do EF, ale chcę mieć model abstrakcji i drzewek wyrażeń, a EF jest i nie chce odbiegać zbyt daleko od stosu MS! – tim

+0

Przepraszam darren, zobacz ten tekst w dalszej odpowiedzi, gdzie będzie on bardziej czytelny! – tim

0

Tak, właśnie to chciałem zrobić Myślę, że następny realease Data Services będzie mógł wykonać właśnie ten LINQ dla zapytań REST, które byłyby świetne w średnim czasie, który właśnie przełączyłem, aby załadować odwrotność i zawrzeć pokrewne podmiot, który zostanie załadowany wiele razy, ale w teorii, to po prostu trzeba ładować raz pierwszy Include jak w tym kodzie

return this.Context.SearchHistories.Include("Handle") 
    .Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate); 

przed próbowałem załadować na dowolny uchwyt do searchHistories pasujący do logiki, ale nie wiem jak wykorzystałem logikę Include, którą opublikowałeś w międzyczasie, myślę, że odwrotne wyszukiwanie byłoby niezbyt brudnym rozwiązaniem

0

Wiem, że to może działać z jednym załączeniem. Nigdy nie testuj z dwoma załącznikami, ale warto spróbować:

dealerships 
    .Include(d => d.parts) 
    .Include(d => d.parts.suppliers) 
    .Where(d => d.parts.All(p => p.price < 100.00) && d.parts.suppliers.All(s => s.country == "brazil")) 
Powiązane problemy