实体框架代码中多对多关系的最佳方法 - 代码

时间:2018-02-28 04:20:25

标签: entity-framework linq many-to-many table-relationships object-relationships

我一直在重新审视我使用实体的方式,我需要建议。

我正在创建一个瑜伽预订服务,每个用户都会有一个简介,空间来满足瑜伽和活动,并有一个日期和时间,以便每个空间聚会。

所以我的关系看起来像这样,他们都是一对多的

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 ICollectionYogaProfile)中的每一个上创建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)的行以及&#39;已注册(YogaProfileId)。

但是现在,看看这种多对多关系设置,我发现我不需要像下面那样向学生(YogaSpaceEvent)添加多个班级(YogaProfile),因为YogaSpaceEvents已添加到YogaSpaces上的集合,而不是YogaProfile

yogaProfile.YogaSpaceEvents.Add(yogaSpaceEvent)

所以我的问题是,我应该回到我最初做的方式还是继续使用这种多对多模型?什么是差异,专业,缺点等?

2 个答案:

答案 0 :(得分:0)

在进一步观察和发现后,我认为这可能是最好的解决方案。

保留我的原始&#39; RegsiteredStudent&#39;实体,但让它看起来像这样

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

我们应该对参考文献都很满意,这样我就可以查找谁...

答案 1 :(得分:0)

如果我错了,请纠正我,但在我看来,个人资料代表一个人。在个人资料可能参加的特定时间内,事件代表瑜伽课程。而Space就是此次活动的举办地点。

空间与事件之间存在一对多关系:在一个地方可能会举行许多活动。每个活动都在一个地方举行。

个人资料和事件之间存在多对多关系:每个个人资料都可以参加许多活动,每个活动都可能有很多个人资料参加。

例如,事件E1保留在空间S1。决定参加活动E1的每个个人资料,他都应该去太空S1。这不应该记录在配置文件中。如果将事件E1移动到另一个空间,例如S2,则只需更改一条记录:与事件和空间相关的记录。这一个更改将使参加E1的所有个人资料都可以看到他们应该转到S2,尽管个人资料中没有任何内容发生变化。

所以我不明白为什么你需要Profiles和Spaces之间的关系。我非常确定,对于定义良好的数据库中的预订服务,您最好不要在个人档案和空格之间建立这样的关系

所以:

  • Space有很多活动,每个活动都在一个空间举行:一对多
  • 个人资料参加许多活动;活动由许多个人资料参加:多对多
  • Profile and 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或属性。

没有保障在同一时间在同一空间举行两个不同的活动。如果你想要,你应该给每个空间零个或多个TimeSlots(每个TimeSlot只属于一个空格)。

为了让事情变得简单,我每个时间段都要花一个小时。超过一小时的事件需要多个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);
    }