Inclui várias referências no segundo nível

87

Suponha que temos este modelo:

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

e

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

Com EF7 eu gostaria de recuperar todos os dados da tabela Tiers, com dados da tabela Contact, da tabela Titre, da tabela TypeContact e assim por diante ... com uma única instrução. Com a API Include / ThenInclude, posso escrever algo assim:

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

Mas depois da propriedade Titre, não posso incluir outras referências como TypeContact, Langue, Fonction ... O método Include sugere objetos Tiers e ThenInclude sugere um objeto Titre, mas não um objeto Contact. Como posso incluir todas as referências da minha lista de contatos? Podemos conseguir isso com uma única instrução?

Christophe Gigax
fonte

Respostas:

153

.ThenInclude()irá acorrentar o último .ThenInclude()ou o último .Include()(o que for mais recente) para puxar em vários níveis. Para incluir vários irmãos no mesmo nível, basta usar outra .Include()corrente. A formatação correta do código pode melhorar drasticamente a legibilidade.

_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
fonte
3
Aliás, essa questão me inspirou a criar a edição nº 2124
bricelam
por que não: var contacts = _dbSet.Include(tiers => tiers.Contacts); contacts.ThenInclude(contact => contact.Titre); contacts.ThenInclude(contact => contact.TypeContact); contacts.ThenInclude(contact => contact.Langue); não funcionaria?
Doug de
1
@Doug Não, você criaria novos Queryableobjetos a cada vez e nunca os avaliaria. contactsteria apenas o valor original que você atribuiu a ele.
bricelam de
e se tiers.Contactsfor um List<T>? como você especificaria o item então?
shashwat
2
Esta solução funciona, mas a instrução SQL resultante resulta em três LEFT JOINs com Contatos (pelo menos na minha experiência). Isso é terrivelmente ineficiente. Tem que haver uma maneira melhor.
EL MOJO
7

Para completar:

Também é possível incluir propriedades aninhadas diretamente via Include , caso não sejam propriedades de coleção, como:

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