2010-07-28 26 views
131

Questa potrebbe essere una domanda davvero elementare, ma quale modo migliore per includere più entità figlio quando si scrive una query che si estende su TRE livelli (o più)?Entity framework linq query Include() più entità minori

cioè Ho 4 tabelle: Company, Employee, Employee_Car e Employee_Country

azienda ha un 1: rapporto m con dipendenti.

Il dipendente ha una relazione di 1: m con Employee_Car e Employee_Country.

Se voglio scrivere una query che restituisce i dati provenienti da tutti e 4 i tavoli, Attualmente sto scrivendo:

Company company = context.Companies 
         .Include("Employee.Employee_Car") 
         .Include("Employee.Employee_Country") 
         .FirstOrDefault(c => c.Id == companyID); 

Ci deve essere un modo più elegante! Questo è prolisso e genera SQL orrendo

Sto usando EF4 con VS 2010

risposta

163

Usa extension methods. Sostituisci NameOfContext con il nome del contesto dell'oggetto.

public static class Extensions{ 
    public static IQueryable<Company> CompleteCompanies(this NameOfContext context){ 
     return context.Companies 
      .Include("Employee.Employee_Car") 
      .Include("Employee.Employee_Country") ; 
    } 

    public static Company CompanyById(this NameOfContext context, int companyID){ 
     return context.Companies 
      .Include("Employee.Employee_Car") 
      .Include("Employee.Employee_Country") 
      .FirstOrDefault(c => c.Id == companyID) ; 
     } 

} 

Poi il codice diventa

 Company company = 
      context.CompleteCompanies().FirstOrDefault(c => c.Id == companyID); 

    //or if you want even more 
    Company company = 
      context.CompanyById(companyID); 
+5

questo è fantastico. –

+0

Ma vorrei usarlo come questo: '// all'interno estensioni pubbliche di classe statiche pubblici statici IQueryable CompleteCompanies (questa tabella DbSet ) { tavolo ritorno .include ("Employee.Employee_Car") .include (" Employee.Employee_Country "); } // codice sarà ... azienda società = context.Companies.CompleteCompanies(). FirstOrDefault (c => c.Id == companyID); // stesso per il prossimo metodo avanzato' – Hamid

+0

Bullsye Nix. Le estensioni dovrebbero essere il primo punto di riferimento per ... beh ... estendere le funzionalità predefinite. – ComeIn

24

si potrebbe trovare questo articolo di interesse di cui dispone codeplex.com.

L'articolo presenta un nuovo modo di esprimere query che si estendono su più tabelle sotto forma di forme di grafico dichiarative.

Inoltre, l'articolo contiene un confronto approfondito delle prestazioni di questo nuovo approccio con le query EF. Questa analisi mostra che GBQ supera rapidamente le richieste EF.

+1

Articolo molto interessante! –

91

EF 4.1 EF 6

C'è un strongly typed .Include che permette la profondità richiesta di carico pronto ad essere specificata fornendo Selezionare espressioni alla profondità appropriata:

using System.Data.Entity; // NB! 

var company = context.Companies 
        .Include(co => co.Employees.Select(emp => emp.Employee_Car)) 
        .Include(co => co.Employees.Select(emp => emp.Employee_Country)) 
        .FirstOrDefault(co => co.companyID == companyID); 

L'SQL generato in entrambe le istanze non sono ancora intuitive, ma sembrano abbastanza performanti.Ho messo un piccolo esempio su GitHub here

EF Nucleo

EF Core ha un nuovo metodo di estensione, .ThenInclude(), anche se la sintassi è slightly different:

var company = context.Companies 
        .Include(co => co.Employees) 
          .ThenInclude(emp => emp.Employee_Car) 
         ... 

Come per la documentazione, ho manterrebbe il "rientro" extra nello .ThenInclude per preservare la sanità mentale.

Info obsoleto (non farlo):

Le molteplici nipoti loading potrebbero essere fatto in un solo passo, ma questo richiede un'inversione piuttosto imbarazzante il backup del grafo prima di scendere al nodo successivo (NB: questo non funziona con AsNoTracking() - si otterrà un errore di runtime):

var company = context.Companies 
     .Include(co => 
      co.Employees 
       .Select(emp => emp.Employee_Car 
        .Select(ec => ec.Employee) 
        .Select(emp2 => emp2.Employee_Country))) 
     .FirstOrDefault(co => co.companyID == companyID); 

quindi vorrei rimanere con la prima opzione (uno Includi per anta entità modello di profondità).

+3

Mi chiedevo come farlo con istruzioni tipizzate .Includere fortemente. Proiettare i bambini con Select è stata la risposta! –

+6

Grazie per aver aggiunto lo spazio dei nomi !!! – GRGodoi

+1

Il mio equiv di "co.Employees.Select (...)" mostra un errore di sintassi su "Seleziona", dicendo che "'Dipendenti' non contiene una definizione per 'Seleziona' [o metodo di estensione]". Ho incluso System.Data.Entity. Voglio solo ottenere una singola colonna dalla tabella unita. –

Problemi correlati