Entity Framework linq query Include () mehrere untergeordnete Entitäten

176

Dies mag eine wirklich elementare Frage sein, aber was ist eine gute Möglichkeit, mehrere untergeordnete Entitäten einzuschließen, wenn Sie eine Abfrage schreiben, die DREI Ebenen (oder mehr) umfasst?

dh ich habe 4 Tabellen: Company, Employee, Employee_CarundEmployee_Country

Das Unternehmen hat eine 1: m-Beziehung zum Mitarbeiter.

Der Mitarbeiter hat eine 1: m-Beziehung zu Employee_Car und Employee_Country.

Wenn ich eine Abfrage schreiben möchte, die die Daten aus allen 4 Tabellen zurückgibt, schreibe ich gerade:

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

Es muss einen eleganteren Weg geben! Dies ist langwierig und erzeugt horrendes SQL

Ich verwende EF4 mit VS 2010

Nathan Liu
quelle

Antworten:

201

Verwenden Sie Erweiterungsmethoden . Ersetzen Sie NameOfContext durch den Namen Ihres Objektkontexts.

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

}

Dann wird Ihr Code

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

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);
Nix
quelle
Aber ich würde es gerne so verwenden: //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. Erweiterungen sollten die erste Anlaufstelle für ... nun ... die Erweiterung vordefinierter Funktionen sein.
Kommen Sie am
12
Jahre später würde ich die stringbasierten Includes nicht empfehlen, da sie nicht zur Laufzeit sicher sind. Wenn sich der Name der Navigationseigenschaft jemals ändert oder falsch geschrieben wird, wird er unterbrochen. Schlagen Sie dringend vor, stattdessen das eingegebene Include zu verwenden.
Jeff Putz
2
Seit der Einführung von nameof (class) ist es möglich, diesen Ansatz sicher anzuwenden. Falls sich der Entitätsname ändert, wird er beim Kompilieren abgeholt. Beispiel: context.Companies.Include (nameof (Employee)) Falls man weiter nach unten gehen muss, müssen die Namen mit nameof (Employee) + "." + Nameof (Employee_Car) übereinstimmen
Karl
Die Erweiterung Methode Technik funktioniert nicht für kompilierte Abfragen (zumindest nicht auf EFCore) hier bestätigt: github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge
156

EF 4.1 bis EF 6

Es ist eine stark getippte.Include , mit dem die erforderliche Tiefe des eifrigen Ladens angegeben werden kann, indem Select-Ausdrücke in der entsprechenden Tiefe bereitgestellt werden:

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

Das in beiden Fällen generierte SQL ist noch keineswegs intuitiv, scheint aber performant genug zu sein. Ich habe ein kleines Beispiel gegeben GitHub gesetzt

EF Core

EF Core hat eine neue Erweiterungsmethode .ThenInclude(), obwohl die Syntax etwas anders ist :

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

Gemäß den Dokumenten würde ich den zusätzlichen Einzug in der behalten .ThenInclude, um Ihre geistige Gesundheit zu bewahren.

Veraltete Informationen (Tun Sie dies nicht):

Das Laden mehrerer Enkelkinder könnte in einem Schritt erfolgen, dies erfordert jedoch eine ziemlich umständliche Umkehrung des Diagramms, bevor der nächste Knoten heruntergefahren wird (Hinweis: Dies funktioniert NICHT AsNoTracking()- Sie erhalten einen Laufzeitfehler):

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

Ich würde also bei der ersten Option bleiben (ein Include pro Blattentitätstiefenmodell).

StuartLC
quelle
4
Ich habe mich gefragt, wie ich das mit stark getippten .Include-Anweisungen machen soll. Die Kinder mit Select zu projizieren war die Antwort!
1
Mein Äquivalent von "co.Employees.Select (...)" zeigt einen Syntaxfehler bei "Select" an und besagt, dass "'Employees' keine Definition für 'Select' [oder Erweiterungsmethode] enthält." Ich habe System.Data.Entity aufgenommen. Ich möchte nur eine einzelne Spalte aus der verknüpften Tabelle erhalten.
Chris Walsh
1
Ich hatte eine übergeordnete Tabelle, die zweimal auf dieselbe untergeordnete Tabelle verwies. Mit der alten String-Include-Syntax war es schwierig, die richtige Beziehung vorzuladen. Dieser Weg ist viel spezifischer. Bitte denken Sie daran, den Namespace System.Data.Entity für stark typisierte Include einzuschließen.
Karl
1
Mit .net Core 2.1 brauchte ich den Namespace Microsoft.EntityFrameworkCore anstelle von System.Data.Entity
denvercoder9
27

Möglicherweise finden Sie diesen Artikel von Interesse, der unter codeplex.com verfügbar ist .

Der Artikel bietet eine neue Möglichkeit, Abfragen, die mehrere Tabellen umfassen, in Form von deklarativen Diagrammformen auszudrücken.

Darüber hinaus enthält der Artikel einen gründlichen Leistungsvergleich dieses neuen Ansatzes mit EF-Abfragen. Diese Analyse zeigt, dass GBQ EF-Abfragen schnell übertrifft.

Merijn
quelle
Wie kann dies in einer realen Anwendung implementiert werden?
Victor.Uduak