【问题标题】:EF code-first optional 1-to-1 relationshipEF 代码优先可选的一对一关系
【发布时间】:2013-07-19 16:12:31
【问题描述】:

我有一个 EF 数据模型,它表示具有报告部分层次结构树的报告。每个 ReportSection 实体包含零个或多个子 ReportSection 的集合。每个 Report 实体都包含一个 ReportSection 实体,用作 ReportSection 树的根。

我的数据模型具有以下导航属性:

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 实体中省略 ReportSectionIdRootReportSection 导航 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 主键。

有没有办法让它工作?具体来说,有没有办法让 Report 实体“包含”顶级 ReportSection 实体,哪个 ReportSection 实体有自己的自引用 ReportSection 实体集合?

【问题讨论】:

    标签: entity-framework-4 ef-code-first


    【解决方案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% 确定。)

    这种与共享主键的一对一关系的缺点是您无法更改ReportRootReportSection 之间的关系,即您不能让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 不会理解这个约束,仍然允许向集合中添加多个项目。但是,如果您尝试保存它,您会从数据库中获得唯一的密钥违规异常。

    【讨论】:

      猜你喜欢
      • 2012-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多