2011-12-08 18 views
6

Se ho una domanda che assomiglia a questo:Entity Framework includono Dove

var forms = repo.GetForms().Where(f => f.SubForms.Any(sf => sf.Classes.Any(c => c.TermId == termId))); 

Da questo si può vedere il mio schema è il seguente:

SubForm ha molti Class che ha molti Term.

quello che voglio:

Tutti SubForms con il loro Classes In una particolare Term.

Quello che sta accadendo ora è che ottengo tutto il SubForm che ha qualsiasi Class in un particolare Term. Ciò significa che SubForm torna con ALL figlio Class e non solo quelli relativi allo Term.

Ad es. Ho 2 termini, una sottomaschera con 2 classi in ogni termine. Questa query riporta 4 classi invece del 2 in quel particolare termine.

C'è qualche Include('Expression') che posso usare per dire che voglio solo includere tutte le classi in base a una condizione? O la mia domanda è sbagliata?

+0

Il primo 'Any' dovrebbe essere un' Where', giusto? Altrimenti la tua query restituirebbe un 'bool', dicendo" Esiste qualche sottomaschera che ha classi con termini con il dato termId, si o no? " – Slauma

+0

@Slauma Scusa, c'è anche un altro livello in cima a quell'espressione, lo modifico, mi dispiace –

risposta

0

non so i nomi esatti dei rapporti, ma dovrebbe essere qualcosa sulla falsariga di

repo.Terms 
    .Include("Classes") 
    .Include("Classes.SubForms") 
    .SingleOrDefault(x => x.TermId = termId); 

// or 

repo.GetSubForms 
    .Include("Classes") 
    .Where(sf => sf.Classes.Where(c => c.TermId == termId)); 
4

Un Include(Where Expression) non esiste. Se usi il caricamento avido con Includi, caricerai sempre tutti gli elementi.

C'è un modo per aggirare questo usando le proiezioni. L'idea di base è selezionare un nuovo tipo anonimo con la proprietà desiderata e un'altra proprietà con gli elementi di navigazione filtrati. EF collegherà quelli insieme e di conseguenza simulerai uno Include(Where ...)

Check this for an example.

+1

Nit minore: 'Include (Espressione)' esiste come System.Data.Entity.DbExtensions.Include il metodo di estensione, ma ha un significato completamente diverso. È usato come alternativa ai letterali stringa: 'repo.Terms.Include (term => term.Classes)'. – hvd

+0

Thnx. Ho modificato la mia risposta –

0

Questa sembra essere una richiesta comune, è stato difficile trovare una soluzione ad esso quando stavo guardando all'inizio di quest'anno. Ho finito per utilizzare la soluzione inclusa nel link sottostante (non sono sicuro che questa sia la soluzione esatta che ho trovato, ma è la stessa idea). Spero che questo ti aiuti!

Filter the "Includes" table on Entity Framework query

   //Found this method to filter our child objects instead of using .include() 
       var Results = (from res in 
            (from u in DataContext.User 
            where u.Type.ToUpper() != "ADMIN" 
            && u.StartDate <= DateTime.Now 
            && (u.EndDate == null || u.EndDate >= DateTime.Now) 
            select new 
            { 
             User = u, 
             Access = u.Access.Where(a => a.StartDate <= DateTime.Now 
                && (a.EndDate == null || a.EndDate >= DateTime.Now)) 
            } 
            ) 
           select res); 

       //The ToArray is neccesary otherwise the Access is not populated in the Users 
       ReturnValue = Results.ToArray().Select(x => x.User).ToList(); 
6

Utilizzare questa:

var subForms = repo.GetSubForms.Select(sf = new { 
     SubForm = sf, 
     Classes = sf.Classes.Where(c => c.TermId == termId) 
    }).ToList() 
    .Select(t => t.SubForm) 
    .ToList(); 

UPDATE: sulla base di @ commento di Slauma:

Se si desidera caricare SubForm s che hanno qualche Class che ha a Term per termId, puoi andare dalla fine iniziare; in questo modo:

var subForms = repo.Terms.Where(t => t.Id == termId).Select(t => new { 
     Term = t, 
     Class = t.Class, 
     SubForm = t.Class.SubForm 
    }).ToList() 
    .Select(t => t.SubForm).ToList(); 

O in un modo più semplice, è possibile utilizzare Include sul Term, vedi:

var subForms = repo.Terms.Include("Class.SubForm").Where(t => t.Id == termId) 
        .Select(t => t.Class.SubForm).ToList(); 

NOTA: Come posso capire dalla tua domanda, si dispone di una rapporto come questo:

SubForm has_many Class has_many Term 

Ma, il codice fornito sta mostrando ar APPORTI come questo:

SubForm has_many Class 
Term has_many Class 

Se è possibile, mettere le entità in questione, o spiegare il loro rapporto più per favore. Grazie.

+1

Hai ancora bisogno del filtro tra 'GetSubForms' e' Select': 'Where (sf => sf.Classes.Any (c => c.TermId == termId))'. Altrimenti si caricheranno * tutte * le sottomaschere dal DB, comprese le sottomascherie con le classi senza termId corrispondente. – Slauma

+0

@Slauma OK, davvero non capisco completamente la domanda; Quindi aggiorno la mia risposta; Grazie :) –

+1

Vero, il primo 'Any' nella domanda è confuso. Ho appena scritto un commento sotto la domanda perché credo che intendesse in realtà un 'Dove'. – Slauma

1

Sapete che a volte comincio a perdersi nei fantastici metodi di estensione LINQ e sto cercando di capire come caricare esattamente ciò che voglio e ricorrere a un concetto di "join" molto semplice.

var result = 
(from f in SubForums 
from c in Classes 
from t in Term 
where t.TermId = 1 
select new { SubForum = f, Class = c, Term = t }).ToList(); 

Questo è un semplice join che utilizza le proprietà di navigazione predefinite (quindi non è necessario specificare la condizione di join). Si restituisce un tipo anonimo con tutto ciò che è necessario. La bellezza di questo è che Entity Framework eseguirà la correzione automatica per te, quindi sei libero di restituire il SubForum solo dal tuo metodo, se lo desideri, conterrà automaticamente la Classe ei successivi termini.