Entity Framework - Schließen Sie mehrere Eigenschaftenebenen ein

376

Die Include () -Methode eignet sich sehr gut für Listen für Objekte. Aber was ist, wenn ich zwei Ebenen tief gehen muss? Die folgende Methode gibt beispielsweise ApplicationServer mit den hier gezeigten enthaltenen Eigenschaften zurück. ApplicationsWithOverrideGroup ist jedoch ein weiterer Container, der andere komplexe Objekte enthält. Kann ich auch ein Include () für diese Eigenschaft ausführen? Oder wie kann ich diese Eigenschaft vollständig laden?

So wie es jetzt aussieht, ist diese Methode:

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

Füllt nur die Enabled-Eigenschaft (unten) und nicht die Application- oder CustomVariableGroup-Eigenschaften (unten). Wie mache ich das möglich?

public class ApplicationWithOverrideVariableGroup : EntityBase
{
    public bool Enabled { get; set; }
    public Application Application { get; set; }
    public CustomVariableGroup CustomVariableGroup { get; set; }
}
Bob Horn
quelle
Hallo, warum ich eine Ausnahme bekomme, Expression must be a member expressionwenn ich Folgendes versuche: So schließen Sie eine Sammlung und dann eine Sammlung eine Ebene tiefer ein : query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection)).
Joe.wang
1
@ BobHorn, ich habe das gleiche Problem. In meinem Fall geht die Verschachtelung tief in mehrere Ebenen, ich habe es geschafft, ein Include zu machen, auf das Sie hingewiesen haben. In der SQL, die generiert wurde, konnte ich sehen, dass alle Spalten mit einem anderen Aliasnamen wie c1, c2 zurückkehren. Meine Frage ist, wie ich aus all meinen Includes eine verschachtelte DTO-Sammlung bilden kann :( .. Vielleicht können Sie das obige Beispiel selbst nehmen, indem wir alle Spalten ohne ein benutzerdefiniertes DTO zurückgeben (das selbst eine Sammlung von DTOs ist )
TechQuery

Antworten:

704

Für EF 6

using System.Data.Entity;

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

Stellen Sie sicher, dass Sie hinzufügen using System.Data.Entity;, um die Version zu erhalten Include, die ein Lambda enthält.


Für EF Core

Verwenden Sie die neue Methode ThenInclude

query.Include(x => x.Collection)
     .ThenInclude(x => x.Property);
Diego Torres
quelle
1
Ich kann Include () nicht für ApplicationsWithOverrideGroup ausführen. Es erscheint nicht in Intellisense.
Bob Horn
Ich kann Ihre Bearbeitung nicht verwenden, da ApplicationsWithOverrideGroup eine Liste ist. Die Anwendung ist eine Eigenschaft für jedes Element in der Liste, nicht für die Liste selbst.
Bob Horn
1
Ahhhh, aber dieser Link, den Sie angegeben haben, scheint die Antwort zu liefern. Lassen Sie mich Folgendes versuchen: Um eine Sammlung und dann eine Sammlung eine Ebene tiefer einzuschließen: query.Include (e => e.Level1Collection.Select (l1 => l1.Level2Collection)).
Bob Horn
60
Denken Sie daran, System.Data.Entity in die Verwendungen aufzunehmen. Andernfalls erhalten Sie von Intellisense nur die Include-Version (Zeichenfolgenpfad) der Methode.
ABl. Raqueño
5
@Adeem müssen Sie Includefür jede Eigenschaft anrufen :Db.States.Include(state => state.Cities.Select(city => city.Customers).Include(state => state.Cities.Select(city => city.Vendors)
Diego Torres
72

Wenn ich Sie richtig verstehe, fragen Sie nach verschachtelten Eigenschaften. Wenn ja :

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

oder

.Include("ApplicationsWithOverrideGroup.NestedProp")  

oder

.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")  
Judo
quelle
6
Danke, das kann ich versuchen. Ich hatte gehofft, in der Lage zu sein, die Dinge stark zu tippen und String-Literale zu vermeiden. Aber wenn es so gemacht werden muss ...
Bob Horn
1
Du warst nah. Möglicherweise war mir nicht klar, dass ApplicationsWithOverrideGroup eine Liste war. Danke fürs Helfen!
Bob Horn
@ Judo, ich habe das gleiche Problem. In meinem Fall geht die Verschachtelung tief in mehrere Ebenen, ich habe es geschafft, ein Include zu machen, auf das Sie hingewiesen haben. In der SQL, die generiert wurde, konnte ich sehen, dass alle Spalten mit einem anderen Aliasnamen wie c1, c2 zurückkehren. Meine Frage ist, wie ich aus all meinen Includes eine verschachtelte DTO-Sammlung bilden kann :( .. Vielleicht können Sie das obige Beispiel selbst nehmen, indem wir alle Spalten ohne ein benutzerdefiniertes DTO zurückgeben (das selbst eine Sammlung von DTOs ist )
TechQuery
2
Denken Sie daran, System.Data.Entity in die Verwendungen aufzunehmen. Andernfalls erhalten Sie von Intellisense nur die Include(string path)Version der Methode.
AlexMelw
52

EF Core: Verwenden von "ThenInclude" zum Laden mehrerer Ebenen: Zum Beispiel:

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
    .ToList();
Thangcao
quelle
53
Sieht so aus, als wäre dies nur EF Core
Chris Marisic
27
Zu Ihrer Information: VS2017 Die Intellisense funktionierte nicht für .ThenInclude. Geben Sie es einfach so ein, wie Sie es sich vorstellen, und die Fehlerhervorhebung sollte verschwinden.
JohnWrensby
4
Ich möchte den Kommentar von @JohnWrensby hervorheben. Die Intellisense kann manchmal besonders lange dauern, um diese ThenInclude zu verarbeiten. Dies kann für neue Benutzer ziemlich verwirrend sein. Ich hatte auch Fälle, in denen der einfache Include-Lambda-Ausdruck nicht richtig behandelt wurde, bis Sie ihn einfach eingeben und kompilieren und die in VS gezeigten "Fehler" ignorieren.
Pac0
@ Pac0 du hast meinen Tag gerettet. Mühe, die untergeordneten Gegenstände zu sehen und konnte nicht.
Bendram
28

Ich habe einen kleinen Helfer für Entity Framework 6 (.Net Core-Stil) erstellt, um Unterentitäten auf nette Weise einzuschließen.

Es ist jetzt auf 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();

Das Paket ist auf GitHub verfügbar .

Lenny32
quelle
Hallo, ich habe eine Ausnahme zur Laufzeit, kann IncludableQueryable <observablecollection> nicht in IncludableQueryable <genericcollection>
umwandeln
Ich benutze zuerst db und habe die tt-Datei geändert, um ObservableCollections für alle meine Entitäten zu erhalten. Jede Hilfe ist willkommen.
user2475096
2
@ lenny32 etwas mit dieser Erweiterung zu beachten?
Aaron Hudon
Beachten Sie, dass dies nicht erforderlich ist, wenn die Eigenschaft, zu der Sie navigieren, eins zu eins mit dem DbSet ist, von dem aus Sie navigiert haben, und Sie DbSet<One>().Include(x => x.Two.Three.Four.Five.Six)mit dem einzigen Nachteil verketten können, dass Sie ein kartesisches Produkt berechnen und möglicherweise die Bandbreite erhöhen.
John Zabroski
23

Weitere EFCore-Beispiele auf MSDN zeigen, dass Sie mit Includeund einige recht komplexe Dinge tun können ThenInclude.

Dies ist ein gutes Beispiel dafür, wie komplex Sie werden können (dies ist alles eine Aussage!):

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();

Sehen Sie, wie Sie Includeauch danach ThenIncludeverketten können, und es setzt Sie auf die Ebene der Entität der obersten Ebene (Instruktoren) zurück.

Sie können dieselbe Sammlung der ersten Ebene (CourseAssignments) sogar mehrmals wiederholen, gefolgt von separaten ThenIncludesBefehlen, um zu verschiedenen untergeordneten Entitäten zu gelangen.

Beachten Sie, dass Ihre eigentliche Abfrage am Ende der Kette Includeoder markiert sein muss ThenIncludes. Folgendes funktioniert NICHT:

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

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

Ich würde Ihnen dringend empfehlen, die Protokollierung einzurichten und sicherzustellen, dass Ihre Abfragen nicht außer Kontrolle geraten, wenn Sie mehr als ein oder zwei Dinge einbeziehen. Es ist wichtig zu sehen, wie es tatsächlich funktioniert - und Sie werden feststellen, dass jedes einzelne "Include" normalerweise eine neue Abfrage ist, um zu vermeiden, dass massive Joins redundante Daten zurückgeben.

AsNoTracking Dies kann die Arbeit erheblich beschleunigen, wenn Sie nicht beabsichtigen, die Entitäten tatsächlich zu bearbeiten und erneut zu speichern.

Simon_Weaver
quelle
Gibt es eine Möglichkeit, sowohl die Einschreibung als auch die Abteilungen ohne Ihre Wiederholung zu erhalten. Beinhaltet für Kurszuweisung und Kurs? (Bisher scheint es, dass die API mit .ThenInclude tiefer gehen kann oder mit .Include zurück auf die oberste Ebene, aber es gibt nichts, was auf dem gleichen Niveau bleiben könnte?)
William Jockusch
Wenn Sie faul laden möchten, bleiben Sie auf dem Laufenden für EF Core 2.1 blogs.msdn.microsoft.com/dotnet/2018/02/02/…, aber wenn Sie nur mehr auf der gleichen Ebene laden möchten, denke ich, dass dies beabsichtigt ist. Ich bin mir nicht sicher, was Sie denken - es erfordert nicht viel mehr, um dies zu tun, und es reduziert das, was aus der Datenbank zurückkommt, erheblich. Eine Entität kann nur ein oder zwei Dinge auf derselben Ebene haben, aber es kann auch 50 für ein großes Projekt haben. Wenn Sie explizit sind, wird Ihre App viel schneller.
Simon_Weaver
Dies war eine gute Erklärung für das Konzept des Include-Zurücksetzens des Levels auf das ursprüngliche Level. Hat mir geholfen, meinen Kopf um die Hierarchie des Include-Systems zu wickeln. Prost!
AFM-Horizon
22

Ich musste auch mehrere Includes verwenden und auf der 3. Ebene brauchte ich mehrere Eigenschaften

(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();

Das kann jemandem helfen :)

dnxit
quelle
1
kann dies ohne Wiederholung getan werden.Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt......
Multinerd
Nun,
7

Lassen Sie mich klarstellen, dass Sie die Zeichenfolgenüberladung verwenden können, um verschachtelte Ebenen unabhängig von der Vielzahl der entsprechenden Beziehungen einzuschließen, wenn Sie nichts dagegen haben, Zeichenfolgenliterale zu verwenden:

query.Include("Collection.Property")
mrmashal
quelle
1
Diese Methode war hilfreich für mich, um herauszufinden, wie dies in VB codiert werden kann, da ich nach stundenlangem Googeln nirgendwo etwas finden kann.
Coder
Das funktioniert super für mich, ich benutze das oft !!! Es funktioniert sogar in Kombination mit .SelectMany-Anweisungen:query.SelectMany(x=>x.foos).Include("bar").Include("bar.docs")...
Ephie