2012-01-03 13 views
6

Próbuję kwerendy Posts na podstawie listy Tags:LINQ wiele-do-wielu skrzyżowaniu

public class Post 
{ 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public virtual ICollection<Tag> Tags {get;set;} 
} 
public class Tag 
{ 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public vritual ICollection<Post> Posts {get;set;} 
} 

Teraz chcę wrócić posty oparte listę tagów: IList<Tag> searchTags = ParseTagsFromSearchString("tag1,tag2,tag3"); // this function checks the tags in the database, so all the primary keys are available in the list

Kiedy post zawiera jeden lub więcej tagów, które istnieją również w searchTags, powinny zostać uwzględnione w wyniku. Próbowałem następujących czynności:

var q = from s in Context.Registrations 
        where s.Tags.Intersect(tagList) 
        select s; 

Błąd: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<Models.Tag>' to 'bool'

var q = from s in Context.Registrations 
        where s.Tags.Any(t => tagList.Any(t2 => t.Id.Value == t2.Id.Value)) 
        select s; 

Runtime error: NotSupportedException: Unable to create a constant value of type 'Models.Tag'. Only primitive types ('such as Int32, String, and Guid') are supported in this context. Jakieś pomysły?

- aktualizacja 4 stycznia: Odpowiedzi wskazują na właściwe rozwiązanie, ale w moim kodzie wciąż mam wyjątek NotSupportedException. Czy jest możliwe, że liczba całkowita zerowalna powoduje to, ponieważ nie jest typem pierwotnym?

Odpowiedz

1

Dziś napotkał ten problem jeszcze raz, i postanowił go rozwiązać. Problem z mojego pytania polega na tym, że IList<Tag> searchTags = ParseTagsFromSearchString("tag1,tag2,tag3"); tworzy listę, która spowoduje wyjątek w przypadku użycia w przecinku wyrażenia innego zapytania w strukturze encji. Właściwą rzeczą jest:

var q = ParseTagsFromSearchString("tag1,tag2,tag3"); // this function will now build a query instead of a list 
IList<Post> posts = (from s in Context.Posts where s.Tags.Intersect(q.AsEnumerable()).Any() select s).ToList(); 
0

Może to może zadziałać?

var q = from s in Context.Registrations 
        where s.Tags.Intersect(tagList).Count() != 0 
        select s; 

czy może

var q = from s in Context.Registrations 
        where s.Tags.Any(t => tagList.Contains(t)) 
        select s; 

nie próbował nic z tego jednak, więc nie ma gwarancji :)

+0

Próbowałem drugie rozwiązanie w kodzie LinqPad postu Marka Lindela i działa.Niestety, w moim kodzie mam ten sam UnsupportedException .. – Marthijn

1

Spróbuj zamienić taglist do listy całkowitej IDS. Użyj tego w zapytaniu, aby naprawić wyjątek NotSupportedException.

var tagIds = tagList.Select(x=>x.Id); 

Następnie użyj tagIds w zapytaniu ...

var q = from s in Context.Registrations 
        where s.Tags.Any(t => tagIds.Any(t2 => t.Id.Value == t2.Id)) 
        select s; 

Nie jestem pewien, czy gniazdowania Wszelkie stwierdzenia typu, które będą faktycznie działa. Po prostu wyjaśniam, dlaczego otrzymujesz wyjątek.

3

Jesteś prawie tam, po prostu zmienić Intersect(taglist) do Intersect(taglist).Any()

Oto przykład roboczych (idąc swoimi definicjami dla Post i Tag):

Tag tag1 = new Tag() { Id = 1, Name = "tag1" }; 
Tag tag2 = new Tag() { Id = 2, Name = "tag2" }; 
Tag tag3 = new Tag() { Id = 3, Name = "tag3" }; 

List<Post> posts = new List<Post>() { 
    new Post() { Id = 1, Name = "post1", Tags = new Tag[] {tag1} }, 
    new Post() { Id = 2, Name = "post2", Tags = new Tag[] {tag2} }, 
    new Post() { Id = 3, Name = "post3", Tags = new Tag[] {tag3} }, 
    new Post() { Id = 4, Name = "post13", Tags = new Tag[] {tag1, tag3} }, 
}; 

List<Tag> searchTags = new List<Tag>() { tag1, tag2 }; 

IEnumerable<Post> matching = posts.Where(p => p.Tags.Intersect(searchTags).Any()); 
//matching now contains post1, post2 and post13 

teraz, w prawdziwym kodzie pewnie wygrał nie używa tych samych instancji dla znaczników na liście wyszukiwania i postach, więc będziesz musiał zastąpić Equals i GetHashCode dla znacznika lub zapewnić IEqualityComparer w wywołaniu do Intersect

+0

Dzięki za odpowiedź, teraz mam ten błąd: 'Nie można utworzyć stałej wartości typu" Models.Tag ". W tym kontekście obsługiwane są tylko typy podstawowe ("takie jak Int32, String i Guid"). – Marthijn

+0

Dodałem działający przykład kodu. Mam nadzieję, że to pomoże. Nie jestem pewien, co próbujesz osiągnąć za pomocą zagnieżdżonego wywołania Any() w aktualizacji. – voidengine

+0

Zaimplementowałem IEqualityComparer, ale dostałem ten błąd: 'LINQ to Entities nie rozpoznaje metody ...'. Jak powiedziałem w moim pytaniu, myślę, że błąd typów pierwotnych jest spowodowany przez "int?". Niestety nie mogę go przetestować w mojej aplikacji, ponieważ część kodu jest już w produkcji. – Marthijn

0

Threw tym razem w LinqPad:

void Main() 
{ 
    var tag1 = new Tag { Name = "tag1" }; 
    var tag2 = new Tag { Name = "tag2" }; 
    var tag3 = new Tag { Name = "tag3" }; 

    var posts = new List<Post> 
    { 
     new Post 
     { 
      Name = "Post1", 
      Tags = new List<Tag> { tag1, tag3 } 
     }, 
     new Post 
     { 
      Name = "Post2", 
      Tags = new List<Tag> { tag2, tag3 } 
     } 
    }; 

    var tagList = new List<Tag> { tag1 }; 

    var q = posts.Where(x => x.Tags.Intersect(tagList).Any()); 

    q.Dump(); 
} 

public class Post 
{ 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public virtual ICollection<Tag> Tags {get;set;} 
} 

public class Tag 
{ 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public virtual ICollection<Post> Posts {get;set;} 
} 
+0

Dzięki, w LinqPad to rzeczywiście działa (ładne narzędzie btw), ale w moim kodzie otrzymuję komunikat "Nie można utworzyć stałej wartości typu" Models.Tag ". W tym kontekście obsługiwane są tylko prymitywne typy (takie jak Int32, String i Guid). Bardzo dziwne, ponieważ ten kod i mój kod są mniej więcej takie same. – Marthijn