Включите несколько ссылок на втором уровне

88

Предположим, у нас есть такая модель:

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

а также

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

С EF7 я хотел бы получить все данные из таблицы Tiers, с данными из таблицы Contact, из таблицы Titre, из таблицы TypeContact и так далее ... с помощью одной единственной инструкции. С помощью API Include / ThenInclude я могу написать что-то вроде этого:

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

Но после свойства Titre я не могу включать другие ссылки, такие как TypeContact, Langue, Fonction ... Метод Include предлагает объекты Tiers, а ThenInclude предлагает объект Titre, но не объект Contact. Как я могу включить все ссылки из моего списка контактов? Можем ли мы добиться этого с помощью одной-единственной инструкции?

Кристоф Гигакс
источник

Ответы:

154

.ThenInclude()будут связаны либо с последним, .ThenInclude()либо с последним .Include()(в зависимости от того, что будет более поздним), на нескольких уровнях. Чтобы включить несколько братьев и сестер на один уровень, просто используйте другую .Include()цепочку. Правильное форматирование кода может значительно улучшить читаемость.

_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
источник
3
Кстати, этот вопрос вдохновил меня на создание выпуска № 2124
bricelam
почему бы и нет: var contacts = _dbSet.Include(tiers => tiers.Contacts); contacts.ThenInclude(contact => contact.Titre); contacts.ThenInclude(contact => contact.TypeContact); contacts.ThenInclude(contact => contact.Langue); разве это не сработает?
Дуг
1
@Doug Нет, вы бы Queryableкаждый раз создавали новые объекты и никогда не оценивали их. contactsбудет иметь только исходное значение, которое вы ему присвоили.
bricelam
что если tiers.Contactsэто List<T>? как бы вы тогда указали товар?
шашват 02
2
Это решение работает, но результирующий оператор SQL приводит к трем ЛЕВЫМ СОЕДИНЕНИЯМ с контактами (по крайней мере, по моему опыту). Это ужасно неэффективно. Там должен быть лучший способ.
EL MOJO
7

Для полноты:

Также можно напрямую включать вложенные свойства, Include если они не являются свойствами коллекции, например:

_dbSet
    .Include(tier => tier.Contact.Titre)
    .Include(tier => tier.Contact.TypeContact)
    .Include(tier => tier.Contact.Langue);
Рисадинья
источник