2013-08-09 19 views
5

Powiedzmy mamy:LINQ wyrażenie filtrować listę kolekcji jednostki i utrzymywać wykaz podmiotów

public class Foo 
{ 
    public long Id { get; set; } 
    public string Name { get; set; } 
    public ICollection<Bar> { get; set; } 
} 

public class Bar 
{ 
    public long Id { get; set; } 
    public int Age { get; set; } 

    public virtual Foo { get; set; } 
    public long FooId { get; set; } 
} 

Nasze dane mogą wyglądać następująco: (zakładając List<Foo>)

// Forget the syntax, just to demonstrate data 
foo[0] = new Foo{ Id = 1, Name = "A", Bar = { collection of Bars with Ages over 10 }}; 
foo[1] = new Foo{ Id = 2, Name = "B", Bar = { collection of Bars with Ages over 20 }}; 
foo[2] = new Foo{ Id = 3, Name = "C", Bar = { collection of Bars with Ages under 10 }}; 

Teraz powiedzmy, że chciałem wszystkie te Foo s, ale z ich Bar s tylko w tym Bar w wieku od 5-25.

Dla czegoś takiego, pracowałbym w odwrotnej kolejności i otrzymywałbym wszystkie Bary, a następnie wszystkie powiązane Foos do tych pasków i ponownie odwzorowywałem Bary z powrotem na Foo. Wydaje się zbyt skomplikowane, niż powinno być.

Aby być bardziej jasne - Wszystko Foos z tylko ich drągi wieku od 5 do 25 :)

+0

http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b – Androiderson

+0

Twoje pytanie jest trochę niejasna - chcesz wszystkich 'Foo's z tylko ich' Bar's z w wieku od 5 do 25 lat, czy chcesz tylko "Foo", które mają "Bar" z wiekiem od 5 do 25? –

+0

Jednym ze sposobów, jeszcze niezalecanym, jest implementacja metody rozszerzenia na Foo z deklaracją podobną do: 'GetBarsBetween (int lower, int upper);' i po prostu użyj tej metody extenion, aby wykonać filtrowanie i pobieranie poprawnych pasów wewnątrz Foo. Następnie możesz po prostu wybrać wszystkie Foo 'gdzie foo.GetBarsBetween (5, 25) .Any()'. Nie wymaga to dodatkowej alokacji memów ani restrukturyzacji twoich obecnych obiektów. Dwie wady, nie można po prostu użyć istniejącego kodu do pracy z filtrowanymi paskami, a czas obliczeń jest nieco wyższy. – Polity

Odpowiedz

4

Jeśli chcesz wybrać wszystkie Foo tych i tylko ich Bar 's pomiędzy wieku z 5 i 25:

var results = 
    from f in db.Foos 
    select new 
    { 
     f.Id, 
     f.Name, 
     Bars = f.Bars.Where(b => b.Age >= 5 && b.Age <= 25) 
    }; 

W wyniku tego powstanie typ anonimowy. Jeśli trzeba utworzyć nazwany typ (na przykład, jeśli trzeba zwrócić wynik z funkcji jako List<T>) powinieneś stworzyć prosty nazwie typ dla tego zestawu Wynik:

public class FooWithFilteredBarResult // replace with whatever name you like 
{ 
    public long Id { get; set; } 
    public string Name { get; set; } 
    public IEnumerable<Bar> { get; set; } 
} 


List<FooWithFilteredBarResult> results = 
    (from f in db.Foos 
    select new FooWithFilteredBarResult 
    { 
     Id = f.Id, 
     Name = f.Name, 
     Bars = f.Bars.Where(b => b.Age >= 5 && b.Age <= 25) 
    }) 
    .ToList(); 
+0

'let' niesie ze sobą dość spore osiągnięcie wydajności w linq do obiektów, tylko do głów. – doogle

+0

Druga odpowiedź na twoje odpowiedzi jest tym, czego szukam. Czy istnieje sposób, aby ominąć konieczność określenia wszystkich właściwości Foo, zamiast tego po prostu "automatycznie wybrać" i umieścić warunkowe na paskach? – Josh

+0

@Josh Nie, niestety nie ma, chociaż zawsze możesz spróbować filtrować je później, np. jak oceniasz wyniki. Musisz tylko określić właściwości 'Foo', które potrzebujesz. Jeśli istnieje 50 innych właściwości, których nie obchodzi, możesz je opuścić. –

1
var r = Foos.Select(x => new Foo() 
     { 
      Id = x.Id, 
      Name = x.Name, 
      Bars = x.Bars.Where(y => y.Age <= 25 && y.Age >= 5).ToList() 
     }); 
+0

Jest to zasadniczo takie samo, jak odpowiedź @pswg # 2, ale nie pozwala mi utworzyć nowego Foo? Mówi, że encji lub typu złożonego nie można skonstruować w kwerendzie LINQ to Entities. – Josh

+0

@Josh wygląda na to, że przesłaliśmy w tym samym czasie. Hmm ... nie wiedziałem, że mówisz o filtrowaniu zapisów przez EF. Pomyśl o tym. – zsong

1

Będzie to również tylko wybrane unikalne Foo.

bars.Where(b => b.Age >= 5 && b.Age <= 25).GroupBy(b => b.FooId).Select(g => g.FirstOrDefault().Foo).ToList(); 
+0

Dostaję GroupBy. Dlaczego Select/FirstOrDefault? – Josh

+0

Aby wybrać tylko różne Foo, w przeciwnym razie możesz skończyć z powtórzeniami zgodnie z pytaniem i strukturą. – doogle

+0

To wydaje się zwracać paski? – Josh

Powiązane problemy