实体框架代码优先的多对多关系的最佳方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实体框架代码优先的多对多关系的最佳方法相关的知识,希望对你有一定的参考价值。
我一直在重新审视我使用实体的方式,我需要建议。
我正在创建一个瑜伽预订服务,每个用户都会有一个简介,空间,以满足瑜伽和活动,有一个日期和时间,每个空间,以满足。
所以我的关系看起来像这样,他们都是一对多的
YogaProfile -> YogaSpace(s) -> YogaSpaceEvent(s)
当一个事件被创建时,成员(YogaProfiles
)可以加入任何人的活动。有点像一个班级。这是一个注册/调度系统。
最初我创建了一个名为RegisterdStudents
的表,并将该集合添加到YogaSpaceEvent
。像这样
public class YogaSpaceEvent
{
// other code here left out
public virtual ICollection<RegisteredStudent> RegisteredStudents { get; set; }
}
和RegisteredStudent
看起来像这样
public class RegisteredStudent
{
[Key]
public int RegisteredStudentId { get; set; }
[Index]
public int YogaSpaceEventId { get; set; }
[ForeignKey("YogaSpaceEventId")]
public virtual YogaSpaceEvent YogaSpaceEvent { get; set; }
[Index]
public int StudentId { get; set; }
}
这一切都很好,但后来我学到了更多关于多对多的知识,并认为我可能需要在这里使用它,因为许多配置文件可以注册一个事件,许多事件可以注册到一个配置文件ex。一个班级可以有很多人参加,一个学生可以参加很多班级。
因此,我通过在两个实体(virtual ICollection
,YogaProfile
)中创建YogaSpaceEvent
并创建一个名为RegisteredStudentInEvent
的连接表来更改代码以建立多对多关系
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<YogaProfile>()
.HasMany<YogaSpaceEvent>(x => x.YogaSpaceEvents)
.WithMany(x => x.RegisteredStudents)
.Map(x =>
{
x.MapLeftKey("YogaProfileId");
x.MapRightKey("YogaSpaceEventId");
x.ToTable("RegisteredStudentInEvent");
});
}
现在我可以成功地将多个学生(YogaProfile
)添加到一个班级(YogaSpaceEvent
),在表格中我看到了带有事件的行(YogaSpaceEventId
)和谁注册了(YogaProfileId
)。
但现在,看看这种多对多的关系设置,我发现我永远不需要像下面那样向学生(YogaSpaceEvent
)添加多个类(YogaProfile
),因为YogaSpaceEvents
被添加到YogaSpaces
的集合中,而不是YogaProfile
yogaProfile.YogaSpaceEvents.Add(yogaSpaceEvent)
所以我的问题是,我应该回到我最初的方式还是继续使用这种多对多模型?有什么区别,专业,缺点等?
在进一步观察和发现后,我认为这可能是最好的解决方案。
保留我原来的'RegsiteredStudent'实体,但看起来像这样
public class RegisteredStudent
{
public RegisteredStudent() { }
[Key]
public int RegisteredStudentId { get; set; }
[Key]
[Column(Order = 1)]
public int YogaProfileId { get; set; }
[Key]
[Column(Order = 2)]
public int YogaSpaceEventId { get; set; }
[ForeignKey("YogaProfileId")]
public YogaProfile YogaProfile { get; set; }
[ForeignKey("YogaSpaceEventId")]
public virtual YogaSpaceEvent YogaSpaceEvent { get; set; }
}
通过这种方式,我可以将注册学生(YogaProfile)添加到这样的新活动中
yogaSpaceEvent.RegsiteredStudents.Add(yogaProfile);
我们应该对参考文献都很满意,这样我就可以查找谁...
如果我错了,请纠正我,但在我看来,个人资料代表一个人。在个人资料可能参加的特定时间内,事件代表瑜伽课程。 Space是此活动举办地点。
空间与事件之间存在一对多关系:在一个地方可能会举行许多活动。每个活动都在一个地方举行。
Profile和Event之间存在多对多关系:每个Profile都可以参加许多活动,每个活动都可能有很多个人参加。
例如,事件E1保存在空间S1。决定参加活动E1的每个个人资料,他都应该去太空S1。这不应该记录在配置文件中。如果将事件E1移动到另一个空间,例如S2,则只需更改一条记录:与事件和空间相关的记录。这一改变将使参加E1的所有配置文件都能看到它们应该转到S2,尽管配置文件中没有任何内容被更改。
所以我不明白你为什么需要Profiles和Spaces之间的关系。我非常确定,对于定义良好的数据库中的预订服务,您最好不要在“个人档案”和“空间”之间建立这样的关系
所以:
- 空间有很多活动,每个活动都在一个空间举行:一对多
- 简介参加许多活动;很多个人资料都是参加活动的对象:多对多
- Profile和Space之间没有直接关系
这为您提供了以下类。
class Space
{
public int Id {get; set;}
// zero or more Events are held at this Space:
public virtual ICollection<Event> Events {get; set;}
... // other properties
}
class Event
{
public int Id {get; set;}
// every event takes place at exactly one Space using foreign key
public int SpaceId {get; set;}
public virtual Space Space {get; set;}
// every Event is attended by zero or more Profiles (many-to-many)
public virtual ICollection<Profile> Profiles {get; set;}
public DateTime StartTime {get; set;}
public TimeSpan Duration {get; set;}
... // other properties
}
class Profile
{
public int Id {get; set;}
// every Profile attend zero or more Events (many-to-many)
public virtual ICollection<Event> Events {get; set;}
... // other properties
}
最后是DbContext:
public MyDbContext : DbContext
{
public DbSet<Event> Events {get; set;}
public DbSet<Space> Spaces {get; set;}
public DbSet<Profile> Profiles {get; set;}
}
因为我遵循了entity framework code first conventions,这就是实体框架需要知道你想要的一对多和多对多关系。实体框架将创建多对多关系所需的外键和额外连接表。不需要Attributes或Fluent API。只有当您需要不同的表名或列名时,才需要流畅的API或属性。
没有保障在同一时间在同一空间举行两个不同的活动。如果你想要,你应该给每个Space零个或多个TimeSlots(每个TimeSlot只属于一个Space)。
为了方便起见,我将每个时段都放一个小时。超过一小时的事件需要多个TimeSlots
class TimeSlot
{
public int Id {get; set;}
public long Hour {get; set;}
// TODO: create a function from Hour to DateTime
// every TimeSlot is a TimeSlot of exactly one Space using foreign key
public int SpaceId {get; set;}
public virtual Space Space {get; set;}
// every TimeSlot is used by one Event using foreign key
public int EventId {get; set;}
public Event Event {get; set;}
}
现在,一个事件在一个或多个TimeSlots中保存(它们是某个空间的TimeSlots)。因此,可以根据需要在一个空间或几个空间中举行一个多小时的活动
要在特定时间在特定空间创建事件,请询问Space是否在请求的时间内有TimeSlots。如果是这样,空间就被占用了。如果没有,空间是免费的。
因此,如果我想创建一个活动,例如晚间课程,则需要在20:00到21:00之间的3个星期三晚上,从2018年5月1日开始,将这三个时间转换为小时:
IEnumerable<long> hours = ... // the three times the course will start
// find a space that has not any TimeSlots during these hours
Space freeSpace = myDbContext.Spaces
.Where(space => !space.TimeSlots
.Select(timeSlot => timeSlot.Hour)
.Union(hour)
.Any())
.FirstOrDefault();
if (freeSpace != null
{ // this Space has no TimeSlot at any of the three hours,
// create an time slots in this Space
IEnumerable<TimeSlot> timeSlots = myDbContext.TimeSlots.AddRange(hours
.Select(hour => new TimeSlot()
{
Hour = hour,
Space = freeSpace,
})
.ToList());
// create an Event to be held at these TimeSlots (and thus at the FreeSpace
var event = myDbContext.Add(new Event()
{
TimeSlots = timeSlots.ToList();
Description = ...
...
});
要让以下个人资料参加此活动:
IEnumerable<Profile> profilesWishingToAttend = ...
foreach (Profile profileWishingToAttend in profilesWishingToAttent)
{
profileWishingToAttend.Events.Add(event);
}
您可以将事件添加到事件中,而不是将事件添加到“个人档案”中:
IEnumerable<Profile> profilesWishingToAttend = ...
foreach (Profile profileWishingToAttend in profilesWishingToAttent)
{
event.Profiles.Add(profileWishingToAttend);
}
以上是关于实体框架代码优先的多对多关系的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章