Query linq di entità framework Includi () più entità figlio


176

Questa potrebbe essere una domanda davvero elementare ma qual è il modo migliore per includere più entità figlio quando si scrive una query che si estende su TRE livelli (o più)?

vale a dire Ho 4 tabelle: Company, Employee, Employee_CareEmployee_Country

La società ha una relazione 1: m con il Dipendente.

Il dipendente ha una relazione 1: m sia con Employee_Car che Employee_Country.

Se voglio scrivere una query che restituisca i dati da tutte e 4 le tabelle, sto attualmente scrivendo:

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

Deve esserci un modo più elegante! Questo è lungo e genera un orrendo SQL

Sto usando EF4 con VS 2010

Risposte:


201

Usa metodi di estensione . 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) ;
      }

}

Quindi il tuo codice diventa

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

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

Ma vorrei usarlo in questo modo: //inside public static class Extensions public static IQueryable<Company> CompleteCompanies(this DbSet<Company> table){ return table .Include("Employee.Employee_Car") .Include("Employee.Employee_Country") ; } //code will be... Company company = context.Companies.CompleteCompanies().FirstOrDefault(c => c.Id == companyID); //same for next advanced method
Hamid,

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

12
Anni dopo, non consiglierei le inclusioni basate su stringhe, perché non sono sicure per il runtime. Se il nome della proprietà di navigazione cambia o è errato, si interromperà. Consiglio vivamente di utilizzare invece l'inclusione digitata.
Jeff Putz,

2
dall'introduzione di nameof (classe) è possibile utilizzare questo approccio in modo sicuro. Nel caso in cui il nome dell'entità cambi, verrà raccolto durante la compilazione. Esempio: context.Companies.Include (nameof (Employee)) Nel caso in cui si debba andare più in basso i nomi devono essere in accordo con nameof (Employee) + "." + Nameof (Employee_Car)
Karl

La tecnica del metodo di estensione non funziona per le query compilate (almeno non su EFCore) confermate qui: github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge

156

Da EF 4.1 a EF 6

Esiste un tipo fortemente tipizzato.Include che consente di specificare la profondità richiesta di caricamento desideroso fornendo espressioni Select 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 entrambi i casi non è ancora affatto intuitivo, ma sembra abbastanza performante. Ho fatto un piccolo esempio su GitHub qui

EF Core

EF Core ha un nuovo metodo di estensione .ThenInclude(), sebbene la sintassi sia leggermente diversa :

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

Secondo i documenti, terrei il "trattino" in più .ThenIncludeper preservare la tua sanità mentale.

Informazioni obsolete (non farlo):

Il caricamento di più nipoti potrebbe essere eseguito in un solo passaggio, ma ciò richiede un'inversione piuttosto imbarazzante del backup del grafico prima di andare al nodo successivo (NB: NON funziona con questo 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 rimarrei con la prima opzione (un modello Includi per profondità dell'entità foglia).


4
Mi chiedevo come farlo con frasi fortemente tipizzate. Includi. Proiettare i bambini con Select è stata la risposta!

1
Il mio equivalente 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.
Chris Walsh,

1
Avevo una tabella padre che faceva riferimento due volte alla stessa tabella figlio. Con la vecchia stringa include la sintassi era difficile precaricare la relazione giusta. In questo modo è molto più specifico. Tieni presente di includere lo spazio dei nomi System.Data.Entity per includere fortemente tipizzato.
Karl

1
Con .net core 2.1 avevo bisogno dello spazio dei nomi Microsoft.EntityFrameworkCore invece di System.Data.Entity
denvercoder9

27

È possibile trovare questo articolo di interesse disponibile su 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 completo delle prestazioni di questo nuovo approccio con le query EF. Questa analisi mostra che GBQ supera rapidamente le query EF.


come può essere implementato in un'applicazione del mondo reale?
Victor.Uduak


0

Potrebbe essere che aiuterà qualcuno, 4 livelli e 2 bambini per ogni livello

Library.Include(a => a.Library.Select(b => b.Library.Select(c => c.Library)))
            .Include(d=>d.Book.)
            .Include(g => g.Library.Select(h=>g.Book))
            .Include(j => j.Library.Select(k => k.Library.Select(l=>l.Book)))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.