EF代码 - 首先是可选的1对1关系

时间:2013-07-19 16:12:32

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

我有一个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实体中省略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主键。

有没有办法让它发挥作用?具体来说,报告实体是否有“包含”顶级ReportSection实体的方法,ReportSection实体有自己的自引用ReportSection实体集合?

1 个答案:

答案 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%肯定。)

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