同一实体类型代码的多个一对多关系优先

Posted

技术标签:

【中文标题】同一实体类型代码的多个一对多关系优先【英文标题】:Multiple one to many relations of the same entity type code first 【发布时间】:2014-02-19 15:57:31 【问题描述】:

我在定义具有两个一对多关系(两个列表)的实体时遇到问题

public class Calendar

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public Guid ID  get; set; 


    public virtual IList<Appointment> FreeSlots  get; set; 
    public virtual IList<Appointment> AppointmentsList  get; set; 
    ...


public class Appointment

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int AppointmentID  get; set; 
    public DateTime Start  get; set; 
    public DateTime End  get; set; 
    public String Type  get; set;        
    public String Info  get; set; 
    public Guid CalendarID  get; set;        
    public virtual Calendar Calendar  get; set; 
 

代码优先代码:

modelBuilder.Entity<Appointment>().HasKey(u => new u.AppointmentID, u.CalendarID );
        modelBuilder.Entity<Appointment>().HasRequired(u => u.Calendar).WithMany(c => c.FreeSlots).HasForeignKey(f => f.CalendarID).WillCascadeOnDelete(true);
        modelBuilder.Entity<Appointment>().HasRequired(u => u.Calendar).WithMany(c => c.AppointmentsList).HasForeignKey(f => f.CalendarID).WillCascadeOnDelete(true);

约会有两个 PK,因为如果删除日历,我希望删除约会。

当我尝试向 FreeSlot 添加新约会时,我收到以下错误: 保存不为其关系公开外键属性的实体时发生错误。 EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。

我也尝试过这个映射,但没有成功:错误 0040:类型 Calendar_FreeSlots 未在命名空间 Project.DAL (Alias=Self) 中定义。

modelBuilder.Entity<Appointment>().HasKey(u => new u.AppointmentID, u.CalendarID );           
        modelBuilder.Entity<Calendar>().HasMany(c => c.FreeSlots).WithRequired(c => c.Calendar).HasForeignKey(c => c.CalendarID).WillCascadeOnDelete();
        modelBuilder.Entity<Calendar>().HasMany(c => c.AppointmentsList).WithRequired(c => c.Calendar).HasForeignKey(c => c.CalendarID).WillCascadeOnDelete();

我想问题是我与同一类型的实体有两个一对多的关系,但我不知道正确的方法。

【问题讨论】:

【参考方案1】:

您的映射中的错误是您对两个不同的关系使用了两次Appointment.Calendar。那是不可能的。您需要Appointment 中的第二对 FK 和导航属性(或映射一个没有反向属性的关系):

modelBuilder.Entity<Calendar>()
    .HasMany(c => c.FreeSlots)
    .WithRequired(c => c.Calendar1)
    .HasForeignKey(c => c.Calendar1ID)
    .WillCascadeOnDelete(false);

modelBuilder.Entity<Calendar>()
    .HasMany(c => c.AppointmentsList)
    .WithRequired(c => c.Calendar2)
    .HasForeignKey(c => c.Calendar2ID)
    .WillCascadeOnDelete(true);

(对于至少一个关系,您必须禁用级联删除。否则您最终会遇到多重级联删除异常。)

但是,我感觉您实际上应该只有一个关系和集合Calendar.AppointmentsAppointment 中的一种状态,例如bool IsFree 属性。然后,您始终可以使用calendar.Appointments.Where(a =&gt; a.IsFree) 提取日历条目的空闲槽。

【讨论】:

谢谢@Slauma!这解决了我的问题。你对 bool 属性是正确的,实际上我使用了两个从 Appointment 继承的不同实体。无论如何,我不明白我的映射有什么问题,但现在我明白了:) 我正在尝试做类似的事情。我有一个包含电子邮件和电话的联系人。我想将电子邮件和电话组合在一个名为 CONTACTINFO 的实体中,该实体可以具有 ContactInfoType 属性(枚举)来区分它们。如果有办法在模型构建器中使用默认 WHERE 子句/条件的导航属性?

以上是关于同一实体类型代码的多个一对多关系优先的主要内容,如果未能解决你的问题,请参考以下文章

实体有两个属性,它们都在一对多关系中引用相同的实体类型

具有多个相同类型的一对多关系的实体和GreenDAO

如何使用同一列休眠设置多个一对多关系

使用 Android 房间数据库在同一张表中的一对多关系

同一实体之间的一对多和多对多关系

为啥 Hibernate 在一对多双向更新操作中给出同一实体的多个表示?