web-dev-qa-db-fra.com

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

C’est peut-être une question très élémentaire, mais qu’est-ce un 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 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 horribles

J'utilise EF4 avec VS 2010

162
Nathan Liu

Utilisez méthodes d'extension . Remplacez NameOfContext par le nom du contexte de votre 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);
188
Nix

EF 4.1 à EF 6

Il existe un fortement typé .Include qui permet de spécifier la profondeur de chargement souhaitée en fournissant des expressions de sélection à 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 here

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

Conformément à la documentation, je garderais le "retrait" supplémentaire dans le .ThenInclude pour préserver votre santé mentale.

Obsolete Info (ne pas le faire):

Le chargement de plusieurs petits-enfants peut être effectué en une étape, mais cela nécessite une inversion assez contraignante pour revenir au graphique 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).

142
StuartLC

Vous pourriez trouver cet article d’intérêt disponible sur codeplex.com .

L'article présente une nouvelle façon d'exprimer des requêtes qui couvrent plusieurs tableaux sous la forme de formes de graphes déclaratifs.

De plus, l'article contient une comparaison approfondie des performances de cette nouvelle approche avec les requêtes EF. Cette analyse montre que GBQ surperforme rapidement les requêtes EF.

27
Merijn

Comment construit-on une requête LINQ to Entities pour charger directement des objets enfants au lieu d'appeler une propriété Reference ou Load ()

Il n'y a pas d'autre moyen - sauf de mettre en place un chargement paresseux.

Ou chargement manuel ....

myobj = context.MyObjects.First();
myobj.ChildA.Load();
myobj.ChildB.Load();
...
5
Andreas Rehm