2012-05-08 64 views
6

Używam relacji wiele do wielu dla moich tabel.LINQ wiele do wielu relacji, jak napisać poprawną klauzulę WHERE?

Jest zapytania:

var query = from post in context.Posts 
     from tag in post.Tags where tag.TagId == 10 
     select post; 

Ok, to działa dobrze. Otrzymuję posty z tagiem określonym przez id.

Mam kolekcję identyfikatorów tagów. Chcę otrzymywać posty zawierające każdy tag z mojej kolekcji.

próbuję następujący sposób:

var tagIds = new int[]{1, 3, 7, 23, 56}; 

var query = from post in context.Posts 
     from tag in post.Tags where tagIds.Contains(tag.TagId) 
     select post; 

to nie działa. Zapytanie zwraca wszystkie posty posiadające DOWOLNY jeden z określonych znaczników.

chcę uzyskać klauzulę tak ale dynamicznie dla każdej liczby znaczników w kolekcji:

post.Tags.Whare(x => x.TagId = 1 && x.TagId = 3 && x.TagId = 7 && ...) 
+1

możliwe duplikat [Jak mogę odzyskać elementy, które są oznaczone ze wszystkim dostarczone tagi w linq?] (http://stackoverflow.com/questions/3478874/how-do-i-retrieve-items-atat-are-tagged-with-all-the-supplied-tags-in-linq) –

Odpowiedz

24

Nie powinny wystawać tagi każdego wpisu w zewnętrznej zapytania; raczej musisz użyć wewnętrznego zapytania, które wykonuje sprawdzenie zewnętrznego filtra. (W SQL, użyliśmy do niej correlated subquery zadzwonić.)

var query = 
    from post in context.Posts 
    where post.Tags.All(tag => tagIds.Contains(tag.TagId)) 
    select post; 

Alternatywny składnię:

var query = 
    context.Posts.Where(post => 
     post.Tags.All(tag => 
      tagIds.Contains(tag.TagId))); 

Edycja: poprawianie za Slauma’s clarification. Poniższa wersja zwraca posty, które zawierają co najmniej wszystkie tagi z kolekcji tagIds.

var query = 
    from post in context.Posts 
    where tagIds.All(requiredId => post.Tags.Any(tag => tag.TagId == requiredId)) 
    select post; 

Alternatywny Składnia

var query = 
    context.Posts.Where(post => 
     tagIds.All(requiredId => 
      post.Tags.Any(tag => 
       tag.TagId == requiredId))); 

Edycja: Poprawione powyżej za Slauma. w tym również inną alternatywę pełne wykorzystanie składni zapytań poniżej:

// Project posts from context for which 
// no Ids from tagIds are not matched 
// by any tags from post 
var query = 
    from post in context.Posts 
    where 
    ( 
     // Project Ids from tagIds that are 
     // not matched by any tags from post 
     from requiredId in tagIds 
     where 
     (
      // Project tags from post that match requiredId 
      from tag in post.Tags 
      where tag.TagId == requiredId 
      select tag 
     ).Any() == false 
     select requiredId 
    ).Any() == false 
    select post; 

mam używany do symulacji .Any() == false operatora w Transact-SQL NOT EXISTS.

+2

+1 za to, że jest jedynym, który pasuje tylko na ** ALL ** tagIds, nie tylko ** ANY ** – mattytommo

+0

@mattytommo Czy jesteś tego pewien? Wydaje mi się, że dostaje wszystkie posty. Edycja: Właśnie dodał post.Tags.All blok, który powinien załatwić sprawę, ale Tejs miał to pierwsze. – Trisped

+0

@Zapisano, nie pasuje ** ALL ** id jest na liście tagIds, nie tylko ** KAŻDY ** – mattytommo

4

To jest rzeczywiście bardzo proste do zrobienia:

var tags = context.Posts.Where(post => post.Tags.All(tag => tagIds.Contains(tag))); 
+0

Chce dopasować ** ALL **, nie tylko 1 – mattytommo

+0

Zaktualizowany, po prostu używając rozszerzenia "Wszystkie". – Tejs

+0

Powoduje to zwracanie wpisów, w których znaczniki * all * występują na liście tagIds. Na przykład: tagIds = ("C#", "Java") i post, który ma tylko tag "C#". Twoje zapytanie zwróci ten wpis. Ale on chce: "dostać posty z ** każdą etykietą ** w mojej kolekcji". I.e .: Tylko posty oznaczone jako "C#" ** i ** "Java". Być może nie jest to łatwe? :) – Slauma

0

Spróbuj z Any.

var query = from post in context.Posts 
    from tag in post.Tags where tagIds.Any(t => t == tag.TagId) 
    select post; 
+0

Chce dopasować ** WSZYSTKO **, nie tylko 1 – mattytommo

4

Inną opcją jest przecinają się dwie listy, jeśli chcesz zbiór tagów zawierają tylko zbiór okreslic i żadne inne:

var query = from post in context.Posts 
    let tags = post.Tags.Select(x => x.Id).ToList() 
    where tags.Intersect(tagIds).Count() == tags.Length 
    select post; 
+1

Nie chcesz "tags.Intersect (tagIds) .Count() == tagIds.Length'? – Trisped

+0

To wygląda dobrze! (ze zmianą w komentarzu @ Trisped) – Slauma

+0

@ Tak, zmieniłem odpowiedź – scottm

Powiązane problemy