Inclure plusieurs références au deuxième niveau

87

Supposons que nous ayons ce modèle:

public class Tiers
{
    public List<Contact> Contacts { get; set; }
}

et

public class Contact
{
    public int Id { get; set; }
    public Tiers Tiers { get; set; }
    public Titre Titre { get; set; }
    public TypeContact TypeContact { get; set; }
    public Langue Langue { get; set; }
    public Fonction Fonction { get; set; }
    public Service Service { get; set; }
    public StatutMail StatutMail { get; set; }
}

Avec EF7, je voudrais récupérer toutes les données de la table Tiers, avec les données de la table Contact, de la table Titre, de la table TypeContact et ainsi de suite ... avec une seule instruction. Avec l'API Include / ThenInclude, je peux écrire quelque chose comme ceci:

_dbSet
     .Include(tiers => tiers.Contacts)
          .ThenInclude(contact => contact.Titre)
     .ToList();

Mais après la propriété Titre, je ne peux pas inclure d'autres références comme TypeContact, Langue, Fonction ... La méthode Include suggère un objet Tiers, et ThenInclude suggère un objet Titre, mais pas un objet Contact. Comment puis-je inclure toutes les références de ma liste de contacts? Pouvons-nous y parvenir avec une seule instruction?

Christophe Gigax
la source

Réponses:

153

.ThenInclude()enchaînera le dernier .ThenInclude()ou le dernier .Include()(selon le plus récent) pour attirer plusieurs niveaux. Pour inclure plusieurs frères et sœurs au même niveau, utilisez simplement une autre .Include()chaîne. Un formatage correct du code peut considérablement améliorer la lisibilité.

_dbSet
    .Include(tiers => tiers.Contacts).ThenInclude(contact => contact.Titre)
    .Include(tiers => tiers.Contacts).ThenInclude(contact => contact.TypeContact)
    .Include(tiers => tiers.Contacts).ThenInclude(contact => contact.Langue);
    // etc.
bricelam
la source
3
BTW, cette question m'a inspiré pour créer le numéro 2124
bricelam
pourquoi pas: var contacts = _dbSet.Include(tiers => tiers.Contacts); contacts.ThenInclude(contact => contact.Titre); contacts.ThenInclude(contact => contact.TypeContact); contacts.ThenInclude(contact => contact.Langue); ça ne marcherait pas?
Doug
1
@Doug Non, vous créeriez de nouveaux Queryableobjets à chaque fois et ne les évalueriez jamais. contactsn'aura jamais que la valeur d'origine que vous lui avez attribuée.
bricelam
et si tiers.Contactsc'est un List<T>? comment spécifieriez-vous l'élément alors?
shashwat
2
Cette solution fonctionne mais l'instruction SQL résultante entraîne trois JOINTS GAUCHE avec des contacts (au moins d'après mon expérience). C'est terriblement inefficace. Il doit y avoir une meilleure façon.
EL MOJO
7

Par souci d'exhaustivité:

Il est également possible d'inclure des propriétés imbriquées directement via Include au cas où elles ne seraient pas des propriétés de collection comme ceci:

_dbSet
    .Include(tier => tier.Contact.Titre)
    .Include(tier => tier.Contact.TypeContact)
    .Include(tier => tier.Contact.Langue);
Risadinha
la source