Requête linq de structure d'entité Inclure () plusieurs entités enfants

entity-framework lazy-loading linq

Question

C’est peut-être une question très complexe, mais quel est le bon moyen d’inclure plusieurs entités enfants lors de l’écriture d’une requête couvrant TROIS niveaux (ou plus)?

c'est-à-dire que j'ai 4 tables: Company , Employee , Employee_Car et Employee_Country

La société entretient une relation de 1: m avec un employé.

L'employé entretient une relation 1: m avec Employee_Car et Employee_Country.

Si je veux écrire une requête qui renvoie les données de toutes les 4 tables, je suis en train d'écrire:

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

Il doit y avoir un moyen plus élégant! C'est long et génère des SQL épouvantables

J'utilise EF4 avec VS 2010

Réponse acceptée

Utilisez des méthodes d'extension . Remplacez NameOfContext par le nom de votre contexte d'objet.

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

}

Ensuite, votre code devient

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

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

Réponse populaire

EF 4.1 à EF 6

Il existe un .Include fortement typé qui permet de spécifier la profondeur de chargement désirée en fournissant des expressions Select à la profondeur appropriée:

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

Le SQL généré dans les deux cas n’est toujours pas intuitif, mais semble assez performant. J'ai mis un petit exemple sur GitHub ici

EF Core

EF Core a une nouvelle méthode d’extension, .ThenInclude() , bien que la syntaxe soit légèrement différente :

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

Selon les documents, je garderais le "retrait" supplémentaire dans le .ThenInclude pour préserver votre santé mentale.

Obsolete Info (Ne faites pas cela):

Le chargement de plusieurs petits-enfants peut être effectué en une seule étape, mais cela nécessite une inversion assez AsNoTracking() avant de redescendre au prochain nœud (NB: cela ne fonctionne PAS avec AsNoTracking() - vous obtiendrez une erreur d'exécution):

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

Je resterais donc avec la première option (un modèle de profondeur d'inclusion par entité feuille).



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow