Entity Framework: include più livelli di proprietà


376

Il metodo Include () funziona abbastanza bene per gli elenchi sugli oggetti. E se avessi bisogno di approfondire due livelli? Ad esempio, il metodo seguente restituirà ApplicationServers con le proprietà incluse mostrate qui. Tuttavia, ApplicationsWithOverrideGroup è un altro contenitore che contiene altri oggetti complessi. Posso fare anche un Include () su quella proprietà? O come posso caricare completamente quella proprietà?

Così com'è ora, questo metodo:

public IEnumerable<ApplicationServer> GetAll()
{
    return this.Database.ApplicationServers
        .Include(x => x.ApplicationsWithOverrideGroup)                
        .Include(x => x.ApplicationWithGroupToForceInstallList)
        .Include(x => x.CustomVariableGroups)                
        .ToList();
}

Popolerà solo la proprietà Enabled (sotto) e non le proprietà Application o CustomVariableGroup (sotto). Come posso farlo accadere?

public class ApplicationWithOverrideVariableGroup : EntityBase
{
    public bool Enabled { get; set; }
    public Application Application { get; set; }
    public CustomVariableGroup CustomVariableGroup { get; set; }
}

Ciao, perché ricevo un'eccezione Expression must be a member expressionquando provo questo: Per includere una raccolta e poi una raccolta di un livello verso il basso: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection)).
Joe.wang

1
@BobHorn, ho lo stesso problema .. Nel mio caso, la nidificazione va in profondità su più livelli, sono riuscito a fare un'inclusione che hai sottolineato. Nell'SQL che è stato generato, ho potuto vedere che tutte le colonne stanno tornando con un nome alias diverso come c1, c2 qualcosa del genere. La mia domanda è: come posso formare una raccolta DTO nidificata da tutte le mie inclusioni :( .. Forse puoi prendere l'esempio sopra stesso, in quanto stiamo restituendo tutte le colonne senza alcun DTO personalizzato (che a sua volta è una raccolta di DTO )
TechQuery,

Risposte:


705

Per EF 6

using System.Data.Entity;

query.Include(x => x.Collection.Select(y => y.Property))

Assicurati di aggiungere using System.Data.Entity;per ottenere la versione Includeche accetta un lambda.


Per EF Core

Usa il nuovo metodo ThenInclude

query.Include(x => x.Collection)
     .ThenInclude(x => x.Property);

1
Non riesco a fare Include () su ApplicationsWithOverrideGroup. Non si presenta in intellisense.
Bob Horn,

Non riesco a utilizzare la modifica perché ApplicationsWithOverrideGroup è un elenco. L'applicazione è una proprietà su ciascun elemento dell'elenco, non sull'elenco stesso.
Bob Horn,

1
Ahhhh, ma quel link che hai fornito sembra fornire la risposta. Vorrei provare questo: per includere una raccolta e poi una raccolta di un livello inferiore: query.Include (e => e.Level1Collection.Select (l1 => l1.Level2Collection)).
Bob Horn,

60
Ricorda di includere System.Data.Entity negli utilizzi. Altrimenti Intellisense ti fornirà solo la versione Includi (percorso stringa) del metodo.
GU Raqueño,

5
@Adeem è necessario chiamare Includeper ogni proprietà:Db.States.Include(state => state.Cities.Select(city => city.Customers).Include(state => state.Cities.Select(city => city.Vendors)
Diego Torres

72

Se ti capisco correttamente, mi stai chiedendo di includere le proprietà nidificate. Se è così :

.Include(x => x.ApplicationsWithOverrideGroup.NestedProp)

o

.Include("ApplicationsWithOverrideGroup.NestedProp")  

o

.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")  

6
Grazie, posso provarlo. Speravo di essere in grado di mantenere le cose fortemente dattiloscritte ed evitare letterali stringa. Ma se è così che deve essere fatto ...
Bob Horn,

1
Eri vicino. Potrei non essere stato chiaro che ApplicationsWithOverrideGroup fosse un elenco. Grazie dell'aiuto!
Bob Horn,

@Judo, ho lo stesso problema .. Nel mio caso, la nidificazione va in profondità su più livelli, sono riuscito a fare un'inclusione che hai sottolineato. Nell'SQL che è stato generato, ho potuto vedere che tutte le colonne stanno tornando con un nome alias diverso come c1, c2 qualcosa del genere. La mia domanda è: come posso formare una raccolta DTO nidificata da tutte le mie inclusioni :( .. Forse puoi prendere l'esempio sopra stesso, in quanto stiamo restituendo tutte le colonne senza alcun DTO personalizzato (che a sua volta è una raccolta di DTO )
TechQuery,

2
Ricorda di includere System.Data.Entity negli utilizzi. Altrimenti Intellisense ti fornirà solo la Include(string path)versione del metodo.
AlexMelw,

53

EF Core: Utilizzo di "ThenInclude" per caricare livelli multipli: Ad esempio:

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
    .ToList();

53
Sembra che questo sia solo EF Core
Chris Marisic, il

27
A proposito: VS2017 per cui l'intellisense non funzionava. Digita semplicemente come pensi che dovrebbe essere e l'evidenziazione degli errori dovrebbe scomparire.
JohnWrensby,

4
Voglio sottolineare il commento di @JohnWrensby, a volte Intellisense può impiegare molto tempo a gestire questi ThenInclude, questo può creare confusione per i nuovi utenti. Ho anche avuto casi in cui la semplice espressione Includi lambda non è stata gestita correttamente, fino a quando non la digiti e la compili, ignorando gli "errori" mostrati in VS.
Pac0,

@ Pac0 mi hai salvato la giornata. lottando per vedere gli oggetti figlio e non poteva.
Bendram,

28

Ho creato un piccolo aiuto per Entity Framework 6 (.Net Core style), per includere le entità secondarie in modo piacevole.

Ora è su NuGet: Install-Package ThenInclude.EF6

using System.Data.Entity;

var thenInclude = context.One.Include(x => x.Twoes)
    .ThenInclude(x=> x.Threes)
    .ThenInclude(x=> x.Fours)
    .ThenInclude(x=> x.Fives)
    .ThenInclude(x => x.Sixes)
    .Include(x=> x.Other)
    .ToList();

Il pacchetto è disponibile su GitHub .


ciao, ho un'eccezione in fase di runtime, non riesco a trasmettere InclableQueryable <raccolta servizi> a IncludiQuery <raccolta generica>
user2475096

sto usando prima db e ho modificato il file tt per ottenere ObservableCollections per tutte le mie entità, qualsiasi aiuto è il benvenuto.
user2475096

2
@ lenny32 qualcosa di cui essere a conoscenza con questa estensione?
Aaron Hudon,

Nota che questo non è necessario se la proprietà verso cui stai navigando è uno a uno con il DbSet da cui hai navigato e puoi concatenarti DbSet<One>().Include(x => x.Two.Three.Four.Five.Six)con l'unico inconveniente che stai calcolando un prodotto cartesiano e potenzialmente aumentando la larghezza di banda.
John Zabroski,

23

Altri esempi di EFCore su MSDN mostrano che puoi fare alcune cose piuttosto complesse con IncludeeThenInclude .

Questo è un buon esempio di quanto tu possa ottenere complesso (questa è tutta una dichiarazione!):

viewModel.Instructors = await _context.Instructors

      .Include(i => i.OfficeAssignment)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)

      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

Scopri come puoi concatenare Includeanche dopo ThenIncludee il tipo di "reimpostazione" ti riporta al livello dell'entità di livello superiore (Istruttori).

Puoi persino ripetere la stessa raccolta di "primo livello" (CourseAssignments) più volte seguita da ThenIncludescomandi separati per accedere a diverse entità figlio.

Nota che la tua query effettiva deve essere taggata alla fine della catena Includeo ThenIncludes. Quanto segue NON funziona:

var query = _context.Instructors.AsQueryable();
query.Include(i => i.OfficeAssignment);

var first10Instructors = query.Take(10).ToArray();

Ti consigliamo vivamente di impostare la registrazione e assicurarti che le tue query non siano fuori controllo se includi più di una o due cose. È importante vedere come funziona effettivamente e noterai che ogni 'inclusione' separata è in genere una nuova query per evitare che enormi join restituiscano dati ridondanti.

AsNoTracking può velocizzare notevolmente le cose se non hai intenzione di modificare effettivamente le entità e salvare.


C'è un modo per ottenere sia l'iscrizione che i dipartimenti senza il tuo ripetuto. Inclusi per corso di assegnazione e corso? (Finora, sembra che l'API possa andare più in profondità con. Poi Include, o tornare al livello più alto con. Includi, ma non c'è nulla per rimanere allo stesso livello?)
William Jockusch

Se vuoi un caricamento lento, resta sintonizzato su EF Core 2.1 blogs.msdn.microsoft.com/dotnet/2018/02/02/…, ma se vuoi semplicemente caricarne di più allo stesso livello, penso che questo sia di progettazione. Non sono sicuro di quello che stai pensando: non richiede molto extra per farlo e riduce notevolmente ciò che ritorna dal database. Un'entità può avere solo una o due cose dello stesso livello ma può anche avere 50 per un progetto di grandi dimensioni, essere espliciti rende la tua app molto più veloce.
Simon_Weaver,

Questa è stata una buona spiegazione del concetto di Includi "ripristinando" il livello di nuovo al livello iniziale. Mi ha aiutato a avvolgere la testa attorno all'erarchia del sistema include. Saluti!
AFM-Horizon,

22

Ho anche dovuto usare più inclusioni e al 3 ° livello avevo bisogno di più proprietà

(from e in context.JobCategorySet
                      where e.Id == id &&
                            e.AgencyId == agencyId
                      select e)
                      .Include(x => x.JobCategorySkillDetails)
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.DurationType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RuleType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RateType))
                      .FirstOrDefaultAsync();

Questo può aiutare qualcuno :)


1
questo può essere fatto senza ripetere.Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt......
Multinerd

beh dipende, quanto in profondità vuoi andare
dnxit

7

Consentitemi di affermare chiaramente che è possibile utilizzare il sovraccarico della stringa per includere i livelli nidificati indipendentemente dalle molteplicità delle relazioni corrispondenti, se non vi dispiace usare i valori letterali delle stringhe:

query.Include("Collection.Property")

1
Questo metodo mi è stato utile per capire come questo può essere codificato in VB, come non riesco a trovare da nessuna parte dopo ore di ricerche su Google.
Coder

Per me funziona benissimo, lo uso molto !!! Funziona anche in combinazione con le dichiarazioni .SelectMany:query.SelectMany(x=>x.foos).Include("bar").Include("bar.docs")...
Ephie,
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.