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