Fluent API, многие-ко-многим в Entity Framework Core 2.0

Я искал stackoverflow для правильного решения при генерации многие-ко-многим отношения, используя EF Core 2.0, сначала код и Fluent API.

простой сценарий будет такой:

public class Person
{
    public Person() {
        Clubs = new HashSet<Club>();
    }
    public int PersonId { get; set; }
    public virtual ICollection<Club> Clubs { get; set; }
}

public class Club
{
    public Club() {
        Persons = new HashSet<Person>();
    }
    public int ClubId { get; set; }
    public virtual ICollection<Person> Persons { get; set; }
}

пожалуйста, поправьте меня, если я ошибаюсь, но я не мог честно найти вопрос, который содержит подробное объяснение того, как это сделать с помощью описанных инструментов. Кто-нибудь может объяснить, как это делается?

3 ответов


это еще невозможно в ядре EF без использования явного класса для соединения. См.здесь пример как это сделать.

открытый вопрос на Github запрашивает возможность сделать это без необходимости явного класса, но он еще не завершен.

используя ваш сценарий, пример, который я связал, рекомендовал бы следующие классы сущностей:

public class Person
{
    public int PersonId { get; set; }
    public virtual ICollection<PersonClub> PersonClubs { get; set; }
}

public class Club
{
    public int ClubId { get; set; }
    public virtual ICollection<PersonClub> PersonClubs { get; set; }
}

public class PersonClub
{
    public int PersonId { get; set; }
    public Person Person { get; set; }
    public int ClubId { get; set; }
    public Club Club { get; set; }
}

следующее будет используется для настройки:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<PersonClub>()
        .HasKey(pc => new { pc.PersonId, pc.ClubId });

    modelBuilder.Entity<PersonClub>()
        .HasOne(pc => pc.Person)
        .WithMany(p => p.PersonClubs)
        .HasForeignKey(pc => pc.PersonId);

    modelBuilder.Entity<PersonClub>()
        .HasOne(pc => pc.Club)
        .WithMany(c => c.PersonClubs)
        .HasForeignKey(pc => pc.ClubId);
}

обязательно перейдите к открытому вопросу, который я связал, и озвучьте свое разочарование, если вы чувствуете необходимость.

EDIT: открытая проблема предлагает использовать простой Select для навигации по этой несколько громоздкой иерархии. Для того, чтобы получить от PersonId коллекция Clubs, Вы можете использовать SelectMany. например:

var clubs = dbContext.People
    .Where(p => p.PersonId == id)
    .SelectMany(p => p.PersonClubs);
    .Select(pc => pc.Club);

Я не могу ручаться за то, что это действительно "лучшая практика", но это, безусловно, должно сделать трюк и я думаю, будет справедливо сказать, что это не слишком уродливо.


правильная "настройка" для этого:

public class Person
{
    public int PersonId { get; set; }
    public virtual ICollection<PersonClub> PersonClubs { get; set; }
}

public class Club
{
    public int ClubId { get; set; }
    public virtual ICollection<PersonClub> PersonClubs { get; set; }
}

public class PersonClub
{
    public int PersonId { get; set; }
    public Person Person { get; set; }
    public int ClubId { get; set; }
    public Club Club { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<PersonClub>()
        .HasKey(pc => new { pc.PersonId, pc.ClubId });
}

Итак, этот блок для настройки "клей-таблицы" -не необходимо, как в Примере @Kirk:

modelBuilder.Entity<PersonClub>()
    .HasOne(pc => pc.Person)
    .WithMany(p => p.PersonClubs)
    .HasForeignKey(pc => pc.PersonId);

modelBuilder.Entity<PersonClub>()
    .HasOne(pc => pc.Club)
    .WithMany(c => c.PersonClubs)
    .HasForeignKey(pc => pc.ClubId);

поэтому каждый Person имеет ноль или более Clubs и все Club имеет ноль или более Persons. Как вы правильно сказали, это правильное отношение "многие ко многим".

вы, вероятно, знаете, что реляционной базе данных нужна дополнительная таблица для реализации этого отношения "многие ко многим". Хорошая вещь о entity framework заключается в том, что она распознает эту связь и создает эту дополнительную таблицу для вас.

на первый взгляд кажется, что этот дополнительный стол не dbSet в своем DbContext: "Как выполнить соединение с этой дополнительной таблице, если у меня нет DbSet за это?".

к счастью, вам не нужно упоминать эту дополнительную таблицу в своих запросах.

Если вам нужен запрос, например: "дайте мне все "клубы"... от каждого человека, кто ...- не думай о соединениях. Вместо того, чтобы использовать ICollections!

получить все лица "Джон Доу" со всеми загородными клубами, которые они посещают:

var result = myDbContext.Persons
    .Where(person => person.Name == "John Doe")
    .Select(person => new
    {
        PersonId = person.Id,
        PersonName = person.Name,
        AttendedCountryClubs = person.Clubs
            .Where(club => club.Type = ClubType.CountryClub),
    };

Entity framework будет признайте, что требуется соединение с дополнительной таблицей "многие ко многим", и выполните это соединение без упоминания этой дополнительной таблицы.

наоборот: получить все загородные клубы с их" Джон Доу " лица:

var result = myDbContext.Clubs
    .Where(club => club.Type = ClubType.CountryClub)
    .Select(club => new
    {
         ClubId = club.Id,
         ClubName = club.Name,
         AnonymousMembers = club.Persons
             .Where(person => person.Name == "John Doe"),
    }

я испытал, что, как только я начал думать в результирующих коллекциях, которые я хочу вместо соединений, мне нужно было получить эти коллекции, я обнаружил, что я едва использую соединения. Это касается отношений "один ко многим", а также отношения "многие ко многим". Entity framework будет внутренне использовать соответствующие соединения.