使用 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);
我的问题是如何设置关系以获取群组中的人员列表?
PersonGroup
是 Event
类型,我需要人员列表类型: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:Events
、Groups
、People
和额外的 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
属性).
我有三个相关实体,这里是使用 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);
我的问题是如何设置关系以获取群组中的人员列表?
PersonGroup
是 Event
类型,我需要人员列表类型: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:Events
、Groups
、People
和额外的 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
属性).