0

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

我正在创建一个瑜伽预订服务,每个用户都会有一个个人资料、瑜伽和活动的聚会空间,以及每个聚会空间的日期和时间。

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

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)

所以我的问题是,我应该回到最初的方式还是继续使用这种多对多模型?有什么区别,优点,缺点等?

4

2 回答 2

0

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

保留我原来的“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);

我们应该对参考资料都很好,这样我就可以查出谁是谁...

于 2018-02-28T04:57:41.527 回答
0

如果我错了,请纠正我,但在我看来,个人资料代表一个人。事件代表个人资料可能参加的特定时间内的瑜伽课之类的事情。空间是此事件发生的位置。

空间和事件之间存在一对多的关系:在一个地方可以举办许多事件。每项活动都在一个地方举行。

Profile和Event之间存在多对多的关系:每个Profile可以参加很多Event,每个Event可以有很多Profile参加。

例如 Event E1 在 Space S1 举行。每个决定参加活动 E1 的 Profile,他都应该去空间 S1。这不应记录在个人资料中。如果将事件 E1 移动到不同的空间,例如 S2,您只需更改一条记录:与事件和空间相关的记录。这一更改将使参加 E1 的所有配置文件都可以看到他们应该去 S2,尽管配置文件中没有任何更改。

所以我不明白为什么你需要配置文件和空间之间的关系。我很确定,对于定义明确的数据库中的预订服务,您最好不要在 Profiles 和 Spaces 之间建立这种关系

所以:

  • 空间有很多活动,每个活动都在一个空间举行:一对多
  • 简介参加了许多活动;活动由许多个人资料参加:多对多
  • 轮廓和空间之间没有直接关系

这为您提供如下类。

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

因为我遵循实体框架代码优先约定,这就是实体框架需要知道的所有内容,即您打算使用一对多和多对多关系。实体框架将创建多对多关系所需的外键和额外的联结表。不需要属性或 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;}
}

现在一个 Event 在一个或多个 TimeSlots(即某个 Space 的 TimeSlots)中举行。因此,一个多小时的活动可以在一个空间或多个空间中举行,如您所愿

要在特定时间在特定空间创建事件,请询问空间在请求的时间是否有时间段。如果是这样,则空间已被占用。如果没有,则空间是免费的。

因此,如果我想创建一个事件,例如一个晚间课程,从 2018 年 5 月 1 日开始,需要 3 个星期三晚上 20:00 到 21:00 之间的时间,请将这三个时间转换为小时:

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);
    }
于 2018-02-28T08:36:13.910 回答