2012-01-03 2 views
6

sto cercando di interrogare Posts sulla base di un elenco di Tags:LINQ molti-a-molti all'incrocio

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;} 
} 

Ora voglio tornare messaggi un elenco delle modifiche basate: 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

Quando un post contiene uno o più tag che esiste anche in searchTags che dovrebbe essere incluso nel risultato. Ho provato la seguente:

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

Errore: 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. Tutte le idee?

- aggiornamento 4 gennaio: Le risposte indicano la soluzione giusta, ma nel mio codice ho ancora NotSupportedException. È possibile che il numero nullable causi questo dato che non è un tipo primitivo?

risposta

1

Oggi ho incontrato di nuovo questo problema, e ha deciso di risolverlo. Il problema della mia domanda è che il IList<Tag> searchTags = ParseTagsFromSearchString("tag1,tag2,tag3"); produce un elenco, che causerà un'eccezione quando utilizzato in un'espressione intersecata di un'altra query nel framework di entità. La cosa giusta da fare è questa:

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

Forse potrebbe funzionare?

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

o forse

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

non abbiamo provato niente di tutto questo, però, così non ci sono garanzie :)

+0

Ho provato la seconda soluzione nel codice LinqPad del post di Mark Lindel e funziona.Purtroppo nel mio codice ho ricevuto la stessa UnsupportedException .. – Marthijn

1

tenta di convertire il vostro TagList a un elenco di numeri interi Ids. Da utilizzare nella query per correggere NotSupportedException.

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

Quindi utilizzare tagIds nella query ...

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

io non sono sicuro se la nidificazione Le dichiarazioni del genere potrà mai funzionare. Sto solo spiegando perché stavi ottenendo l'eccezione.

3

Sei quasi, basta cambiare il Intersect(taglist)-Intersect(taglist).Any()

Ecco un esempio di lavoro (percorrendo le definizioni dei per Post e 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 

ora, nel codice reale probabilmente vinto utilizzare le stesse istanze per i tag nell'elenco di ricerca e per i post, quindi sarà necessario eseguire l'override di Equals e GetHashCode per Tag o fornire un IEqualityComparer nella chiamata a Intersect

+0

Grazie per la risposta, ora ho ricevuto questo errore: 'Impossibile creare un valore costante di tipo 'Models.Tag'. Solo i tipi primitivi ('come Int32, String e Guid') sono supportati in questo contesto. – Marthijn

+0

Ho aggiunto un esempio di codice funzionante. Spero che sia d'aiuto. Non sono sicuro di cosa stai cercando di ottenere con la chiamata Any() annidata nel tuo aggiornamento. – voidengine

+0

Ho implementato un IEqualityComparer, ma ho ricevuto questo errore: 'LINQ to Entities non riconosce il metodo ...'. Come ho detto nella mia domanda, penso che l'errore dei tipi primitivi sia causato da 'int?'. Purtroppo non posso testarlo nella mia app perché parte del codice è già in produzione. – Marthijn

0

ha gettato questo insieme in 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

Grazie, in LinqPad funziona davvero (bel tool btw), ma nel mio codice ottengo 'Impossibile creare un valore costante di tipo 'Models.Tag'. Solo i tipi primitivi ('come Int32, String e Guid') sono supportati in questo contesto. Molto strano visto che questo codice e il mio codice sono più o meno gli stessi. – Marthijn