流畅的API,实体框架核心中的多对多

时间:2017-09-12 20:15:37

标签: c# entity-framework code-first ef-fluent-api

我已经使用EF Core,Code first和Fluent API搜索了stackoverflow以获得生成多对多关系的正确解决方案。

一个简单的场景是:

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 个答案:

答案 0 :(得分:27)

在没有使用显式类进行连接的情况下,EF Core中尚无法实现。有关如何执行此操作的示例,请参阅here

Github上有一个开放的issue,要求能够在不需要明确的课程的情况下完成这项任务,但尚未完成。

使用您的方案,我链接的示例将推荐以下实体类:

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

然后将使用以下OnModelCreating进行设置:

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

如果您觉得有必要,请务必转到我链接的未解决的问题并表达您的挫败感。

编辑:开放式问题建议使用简单的Select来浏览这个有点繁琐的层次结构。要从PersonId转到Club的集合,您可以使用SelectMany。 e.g:

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

我无法保证这是否真的是一种最好的做法&#34;,但它肯定应该做到这一点,我认为公平地说它并不过分丑陋。< / p>

答案 1 :(得分:13)

正确的“设置”是:

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

答案 2 :(得分:0)

因此,每个Person都有零个或多个Clubs,而每个Club都有零个或多个Persons。正如你所说,这是一个恰当的多对多关系。

您可能知道关系数据库需要一个额外的表来实现这种多对多关系。关于实体框架的好处是它能够识别这种关系并为你创建这个额外的表。

乍一看,这个额外的表在dbSet中不是DbContext似乎是一个问题:“如果我没有{{1},如何使用这个额外的表执行连接对于它?“。

幸运的是,您无需在查询中提及此额外表格。

如果你需要一个像“给我所有'俱乐部'那样的查询......来自每个'人'......”不要考虑加入。而是使用ICollections!

让所有参加乡村俱乐部的所有“John Doe”成员:

DbSet

实体框架将识别需要具有额外多对多表的连接,并且将执行此连接,而无需提及此额外表。

反过来说:通过他们的“John Doe”人来​​获得所有乡村俱乐部:

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

我经历过,一旦我开始考虑我想要的结果集合而不是我需要获得这些集合的连接,我发现我几乎不使用连接。这是一对多关系以及多对多关系的情况。实体框架将在内部使用正确的连接。

相关问题