Fluent API,Entity Framework Core 2.0中的多对多

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

一个简单的场景是:

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

如果我错了,请纠正我,但我老实说没有找到一个问题,其中包含如何使用所描述的工具进行详细说明。 谁能解释一下这是怎么做到的?

如果不使用显式类进行连接,则在EF Core中尚不可能。 请参阅此处以获取如何执行此操作的示例。

Github上存在一个未解决的问题 ,要求能够在不需要显式类的情况下执行此操作,但尚未完成。

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

 public class Person { public int PersonId { get; set; } public virtual ICollection PersonClubs { get; set; } } public class Club { public int ClubId { get; set; } public virtual ICollection 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() .HasKey(pc => new { pc.PersonId, pc.ClubId }); modelBuilder.Entity() .HasOne(pc => pc.Person) .WithMany(p => p.PersonClubs) .HasForeignKey(pc => pc.PersonId); modelBuilder.Entity() .HasOne(pc => pc.Club) .WithMany(c => c.PersonClubs) .HasForeignKey(pc => pc.ClubId); } 

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

编辑:开放的问题建议使用一个简单的Select来浏览这个有点繁琐的层次结构。 要从PersonId获取Club的集合,您可以使用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 PersonClubs { get; set; } } public class Club { public int ClubId { get; set; } public virtual ICollection 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() .HasKey(pc => new { pc.PersonId, pc.ClubId }); } 

因此,在@Kirk示例中,不需要用于配置“glue-table”的块:

 modelBuilder.Entity() .HasOne(pc => pc.Person) .WithMany(p => p.PersonClubs) .HasForeignKey(pc => pc.PersonId); modelBuilder.Entity() .HasOne(pc => pc.Club) .WithMany(c => c.PersonClubs) .HasForeignKey(pc => pc.ClubId); 

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

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

乍一看,这个额外的表不是dbSet中的DbContext似乎是一个问题:“如果我没有DbSet ,如何使用这个额外的表执行连接?”。

幸运的是,您不需要在查询中提及这个额外的表。

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

让他们参加的所有乡村俱乐部的所有“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), }; 

entity framework将认识到需要与额外的多对多表的连接,并且将执行此连接,而无需提及此额外表。

反过来:让所有乡村俱乐部的“John Doe”人员:

 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将在内部使用正确的连接。