使用 Fluent API 的 EF 6 Code First 关系。如何设置第一个和第三个表之间的关系或获取分组集合

EF 6 Code First relationships using Fluent API. How to set relation between first and third tables or get grouped collection

我有三个相关实体,这里是使用 Fluent 声明的结构和关系 API

一个活动,参加的人很多(实际上是一群人)所以有可能GroupID把他们都收集起来。那么如何做到这一点呢?

public class Event
{
    public int EventID { get; set; }
    public string DocID { get; set; }
    public string GroupID { get; set; }
    public virtual Person Person { get; set; }
    public virtual Group Group { get; set; }
    public virtual ICollection<Person> GroupPerson {get; set}
}

人实体,这里我有关于人的所有信息,如名字、姓氏、生日...

public class Person
{
    public string PersonID { get; set; }
    public string PersonName { get; set; }
    public string PersonSurName { get; set; }
    public string PersonCode { get; set; } 
}    

群组实体,这里是关于群组的信息

public class Group
{
    public string GroupID { get; set; }
    public string GroupName { get; set; }
    public virtual ICollection<Event> EventGroup { get; set; }
}

现在我用 Fluent API 描述关系。首先是主键:

modelBuilder.Entity<Event>().HasKey(e => e.EventID);  
modelBuilder.Entity<Person>().HasKey(e => e.PersonID);  
modelBuilder.Entity<Group>().HasKey(e => e.GroupID);

这里我会有活动相关人员

modelBuilder.Entity<Event>()
                .HasRequired(s => s.Person)
                .WithMany()
                .HasForeignKey(fk=> fk.PersonID); 

在这里我将有 PersonGroup

 modelBuilder.Entity<Group>()
               .HasKey(e => e.GroupID)
               .HasMany(e => e.EventGroup)
               .WithOptional()
               .HasForeignKey(f => f.GroupID);

我的问题是如何设置关系以获取群组中的人员列表? PersonGroupEvent 类型,我需要人员列表类型:Person => ICollection<Person> GroupPerson in Event class.

鉴于你们的关系是这样的:

  • 一个事件恰好(与)一组(必填)
  • 一组有零到多个事件(相关)
  • 一组有零到多人(相关)
  • 一个人有零到多个组(相关)

也就是说,您的关系 Events-Groups 是一对多的,而您的关系 Groups-People 是多对多的(我假设同一个人可以在多个组中)。 Events和People之间没有直接关系,而是Event -> Group -> People的传递关系。

那么可以这样建模:

public class Event
{
    public int EventID { get; set; }
    public string DocID { get; set; }
    public string GroupID { get; set; }
    public virtual Group Group { get; set; }
    public virtual ICollection<Person> People { get { return Group.People; } }
}

public class Person
{
    public string PersonID { get; set; }
    public string PersonName { get; set; }
    public string PersonSurName { get; set; }
    public string PersonCode { get; set; }
}

public class Group
{
    public string GroupID { get; set; }
    public string GroupName { get; set; }
    public virtual ICollection<Event> Events { get; set; }
    public virtual ICollection<Person> People { get; set; }
}

DbContext 中使用这些 DbSets:

public DbSet<Person> People { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Event> Events { get; set; }

而这个 EF 配置:

modelBuilder.Entity<Event>()
    .HasKey(e => e.EventID)
    .Ignore(e => e.People)
    .HasRequired(e => e.Group)
    .WithMany(g => g.Events);

modelBuilder.Entity<Group>()
    .HasKey(g => g.GroupID)
    .HasMany(g => g.People)
    .WithMany();

 modelBuilder.Entity<Person>()
    .HasKey(p => p.PersonID);

请注意 Event.People 有明确的 Ignore()。这是因为 Event 和 Person 之间的关系是可传递的,您不需要在数据库中为此添加额外的列。如果您不明白为什么,请尝试注释掉 Ignore() 行并重新生成迁移,然后会看到在 People table 中为事件 ID 生成了一个额外的列(此列不会很有道理)。

因此 Events 中的 People 属性 未由 EF 填充,您必须自己完成:

public virtual ICollection<Person> People { get { return Group.People; } }

要将人员添加到活动中,您应该使用群组导航 属性,如下所示:

public class Event
{
    ...

    public void AddPerson(Person p)
    {
        this.Group.People.Add(p);
    }
}

使用此代码,迁移是这样生成的,有四个 tables:EventsGroupsPeople 和额外的 table PeopleGroups 用于Person和Group之间的多对多关系。

    public override void Up()
    {
        CreateTable(
            "dbo.Events",
            c => new
                {
                    EventID = c.Int(nullable: false, identity: true),
                    DocID = c.String(),
                    GroupID = c.String(nullable: false, maxLength: 128),
                })
            .PrimaryKey(t => t.EventID)
            .ForeignKey("dbo.Groups", t => t.GroupID, cascadeDelete: true)
            .Index(t => t.GroupID);

        CreateTable(
            "dbo.Groups",
            c => new
                {
                    GroupID = c.String(nullable: false, maxLength: 128),
                    GroupName = c.String(),
                })
            .PrimaryKey(t => t.GroupID);

        CreateTable(
            "dbo.People",
            c => new
                {
                    PersonID = c.String(nullable: false, maxLength: 128),
                    PersonName = c.String(),
                    PersonSurName = c.String(),
                    PersonCode = c.String(),
                })
            .PrimaryKey(t => t.PersonID);

        CreateTable(
            "dbo.GroupPersons",
            c => new
                {
                    Group_GroupID = c.String(nullable: false, maxLength: 128),
                    Person_PersonID = c.String(nullable: false, maxLength: 128),
                })
            .PrimaryKey(t => new { t.Group_GroupID, t.Person_PersonID })
            .ForeignKey("dbo.Groups", t => t.Group_GroupID, cascadeDelete: true)
            .ForeignKey("dbo.People", t => t.Person_PersonID, cascadeDelete: true)
            .Index(t => t.Group_GroupID)
            .Index(t => t.Person_PersonID);

    }

如果你不喜欢关系中列的名称 table GroupPersons 你可以添加一个 .Map() 配置(但你真的不需要这样做,因为这个 table 没有直接使用,所以没有它的模型实体,它甚至没有 DbContext 中的 DbSet 属性).