2015-06-22 18 views
5

Ho le seguenti 2 entità nel mio db.Contiene almeno il framework Entity

public class Article 
{ 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    // Some code removed for brevity 

    public virtual ICollection<Tag> Tags { get; set; } 
} 


public class Tag 
{ 

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    // Some code removed for brevity 

    public virtual ICollection<Article> Articles { get; set; } 

} 

Ho bisogno di filtrare questi articoli in base agli ID tag che sono passati nella mia azione.

public ActionResult FindAll(List<int> tags) 
{ 

    // 
    // I need to return all articles which have ALL the tags passed into this method 
    // 

    var query = ApplicationDbContext.Articles... 


} 

Per esempio, se ho passato in 1, 2, 3 in azione, solo gli articoli che hanno avuto questi 3 tag o più sarebbe ritornato.

Come posso ottenere questo risultato?

Grazie per l'ottima risposta!

Tutte le risposte hanno prodotto il risultato corretto, quindi ho eseguito una rapida profilazione di base in sql e questo è stato il risultato in base alle tue domande.

Query Results

+0

Per i '' risposte all' Except' e, si potrebbe desiderare di controllare che le prestazioni sono accettabili per il carico ti aspetti, e che l'SQL generato funziona ancora se 'tags.Count' è grande. Vedi i commenti sotto [questo] (http://stackoverflow.com/questions/30947278/ef-intersect-syntax/30948394#comment49949396_30947688) per maggiori dettagli. * Potrebbe * valere la pena di interrogare un superset di articoli (con uno qualsiasi dei tag?) E ulteriori filtri. – jjj

+0

se nessuna delle risposte ha funzionato o hai ancora problemi, fammi sapere così posso aiutarti – AmmarCSE

+0

Sono curioso: quanti tag hai usato per questi test, quanto era grande la tabella 'Articoli' e quanto grande era il set di risultati? – jjj

risposta

3

Usa Except() e Any() come

ApplicationDbContext 
    .Articles 
    .Where(a => tags.Except(a.Tags.Select(t => t.Id).ToList()).Any() == false) 

Except() vi darà gli elementi dalla prima lista che non facciamo esiste nel secondo elenco

L'operatore Tranne produce la differenza tra due sequenze. Restituisce solo gli elementi nella sequenza prima che non compaiono nello nella seconda.

+0

Questo funziona solo per gli articoli che hanno solo i tag corrispondenti e non di più .... giusto? – br4d

+0

Non penso che questo faccia ciò che l'OP vuole, ha dichiarato che desidera * almeno * i tag specificati, non * solo *. – adam0101

+0

@ br4d, buon punto.Ive ha corretto la mia risposta – AmmarCSE

1

Creazione in modo approssimativo del risultato tramite IQueryable.

public ActionResult FindAll(List<int> tags) 
{ 
    var queryable = ApplicationDbContext.Articles.AsQueryable(); 

    foreach(var t in tags) 
    { 
     queryable = queryable.Where(w => w.Tags.Any(a => a.Id == t)); 
    } 

    queryable.AsEnumerable(); // stuff this into a viewmodel and return actionresult? 
} 
+0

Grazie per la tua risposta! Ho visto in un altro commento che hai provato a fare questo come un solo rivestimento. Questo è possibile usando il metodo Aggregate. tags.Aggregate (ApplicationDbContext.CaseStudies.AsQueryable(), (current, t) => current.Where (w => w.Tags.Any (a => a.Id == t))); – heymega

0

Utilizzare questa:

ApplicationDbContext.Articles.Where(a => tags.All(t => a.Tags.Contains(t))); 
+0

Questo non è corretto. Vuole abbinare tutti i tag specificati, non tutti i tag nell'articolo. – adam0101

+0

Risposta aggiornata. – ranquild

+0

Sembra proprio ora che il tag article sia un oggetto con una proprietà Id, non un intero. – adam0101

2

Questo dovrebbe farlo:

ApplicationDbContext.Articles.Where(a => tags.All(t => a.Tags.Any(at => at.Id == t))); 
+0

Non ho mai nemmeno saputo che esistesse un "tutto". Bello! – AmmarCSE

2

Prova questa:

var query =from a in ApplicationDbContext.Articles 
      where a.Tags.Count(t => tags.Contains(t.Id)) == tags.Count 
      select a; 
+0

Penso che la tua prima revisione fosse corretta. Questo ora corrisponde al numero esatto di tag, che non è quello che l'OP voleva. – adam0101

+0

@ adam0101, la prima query ha restituito gli articoli con almeno un tag che corrisponde nell'elenco dei tag. Ora, con la seconda query non sto solo controllando il numero di tag uguali, controlla la condizione nella sottoquery, se la quantità di tag contenuti corrisponde alla quantità di elementi nell'elenco 'tags', quindi seleziono l'articolo – octavioccl

+2

'dove a.Tags.Count (t => tags.Contains (t.Id)) == tag.Count'? – jjj

1

ne dici di questo?

var articles = ApplicationDbContext.Articles.Where (a => a.Tags.Select (t => t.Id).Intersect(tags).Count()>=tags.Count);