我有一个EF数据模型,它代表一个包含报告部分分层树的报告。每个ReportSection实体都包含零个或多个子ReportSections的集合。每个Report实体都包含一个ReportSection实体,该实体充当ReportSections树的根。
我的数据模型具有以下导航属性:
public class Report
{
// Primary key
public int Id { get; set; }
// A Report has one root ReportSection
[ForeignKey("RootReportSection")]
public int ReportSectionId { get; set; }
public virtual ReportSection RootReportSection { get; set; }
}
public class ReportSection
{
// Primary key
public int Id { get; set; }
// Optional self-reference to the parent ReportSection
[ForeignKey("ParentReportSection")]
public int? ParentReportSectionId { get; set; }
public virtual ReportSection ParentReportSection { get; set; }
// Optional foreign key to the parent Report
[ForeignKey("ParentReport")]
public int? ReportId { get; set; }
public virtual Report ParentReport { get; set; }
// Child ReportSections contained in this ReportSection
public virtual ICollection<ReportSection> ReportSections { get; set; }
}
如果我从Report实体中省略ReportSectionId
和RootReportSection
导航claptrap,那么一切正常。但是,如上所述,尝试添加迁移会收到错误:
Because the Dependent Role properties are not the key properties,
the upper bound of the multiplicity of the Dependent Role must be '*'.
经过一番挖掘,我现在明白EF显然希望我使用ReportSections实体的主键作为Report实体的外键。但是,在我的场景中,只有ReportSection实体的分层树中的顶级ReportSection参与与Report实体的关系。其余的ReportSection实体彼此相关,其主键独立于任何Report主键。
有没有办法让它发挥作用?具体来说,报告实体是否有“包含”顶级ReportSection实体的方法,ReportSection实体有自己的自引用ReportSection实体集合?
答案 0 :(得分:1)
显然ReportSection
是关系中的主体和Report
依赖关系(因为Report
必须引用现有RootReportSection
因为Report.ReportSectionId
不可为空) 。在这种情况下,ReportSection
很可能没有相关的Report
。您的所有孩子ReportSection
都没有Report
。
但这只有在Report
中的密钥不会自动生成时才有效,因为密钥必须与相关(并且已经存在)RootReportSection
的密钥相同。所以,你可以试着像这样建模:
public class Report
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("RootReportSection")]
public int Id { get; set; }
public virtual ReportSection RootReportSection { get; set; }
}
public class ReportSection
{
public int Id { get; set; }
// Optional self-reference to the parent ReportSection
[ForeignKey("ParentReportSection")]
public int? ParentReportSectionId { get; set; }
public virtual ReportSection ParentReportSection { get; set; }
// Optional foreign key to the parent Report
public virtual Report ParentReport { get; set; }
// Child ReportSections contained in this ReportSection
public virtual ICollection<ReportSection> ReportSections { get; set; }
}
([DatabaseGenerated(DatabaseGeneratedOption.None)]
可能是多余的,因为EF知道密钥不能在这种一对一关系中生成数据库。虽然我不是100%肯定。)
与共享主键的这种一对一关系的缺点是,您无法更改Report
和RootReportSection
之间的关系,即您无法让Report
引用除了具有相同主键的部分之外的任何其他部分。
如果这对您不起作用,则必须将关系建模为一对多,因为EF不支持使用单独的外键进行一对一关系。要么完全删除那部分......
[ForeignKey("ParentReport")]
public int? ReportId { get; set; }
public virtual Report ParentReport { get; set; }
...如果您不需要从该部分导航到报告或将其替换为集合:
public virtual ICollection<Report> ParentReports { get; set; }
您必须确保在业务逻辑中,此集合中不会添加多个报表,以便对一对一关系进行模拟。在数据库中,您可以在Report.ReportSectionId
上添加唯一约束,以在数据库级别上保持数据完整性。但是EF不会理解这个约束,仍然允许在集合中添加多个项目。但是,如果您尝试保存它,则会从数据库中获得唯一的密钥违例异常。