Entity Framework 循环关系的外键

Entity Framework Foreign Key For Circular Relation

我正在将一个应用程序转换为 Entity Framework 核心,运行我在获取我的两个模型 类 之间的外键关系时遇到了麻烦。 类 设置如下(注意 Guid Id 字段在 BaseEntity 上声明):

public class Crt : BaseEntity
{
    [Required]
    public Guid FacId { get; set; }

    [Required]
    public string Code { get; set; }

    [ForeignKey("ActiveCrtChk") 
    public Guid? ActiveCrtChkId { get; set; }

    public string Description { get; set; }

    public string Device { get; set; }

    #region navigation properties
    public CrtChk ActiveCrtChk;
    public List<CrtChk> CartChecks;
    #endregion
}

public class CrtChk : BaseEntity
{
    [Required]
    public Guid CrtId { get; set; }

    [Required]
    public string Device { get; set; }

    [Required]
    public Guid OutSysUsrId { get; set; }

    [Required]
    public DateTime OutSysDateTime { get; set; }

    public Guid? InSysUsrId { get; set; }

    public DateTime? InSysDateTime { get; set; }

    [Required]
    public string Type { get; set; }

    #region navigation properties
    public Crt Cart { get; set; }
    public Usr OutSysUsr { get; set; }
    public Usr InSysUsr { get; set; }
    public List<CrtEvt> CartEvents { get; set; }
    #endregion
}

关系背后的想法是一个 Crt 可以有许多 CrtChk 记录,但是 Crt 还存储 活动的 Id CrtChk 记录。

当我 运行 迁移时,它会生成我期望的 CrtCrtChk 之间的所有外键关系,但没有为 [=22] 生成外键=] 字段。

据我阅读 this post 的理解,ActiveCrtChkId 属性 上的 ForeignKey 属性带有 ActiveCrtChk 导航 属性,我应该在迁移中获得外键约束。

我在这里错过了什么?

编辑

修复我将 Crt 导航属性声明为字段的错误后,我在尝试创建迁移时遇到了一个新错误。

Unable to determine the relationship represented by navigation property 'Crt.ActiveCrtChk' of type 'CrtChk'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

我以为 ForeignKey 属性是手动配置关系?我是否需要使用 Fluent API 来创建关系?如果是这样,我如何使用 Fluent API 建立一对一(CrtActiveCrtChk)和一对多(所有 CrtChks 与 Crt)?

这是可能的,但由于这种设计会在两个实体之间产生循环依赖,因此会给您带来很多问题。例如,不仅其中一个关系(比如从 CrtChkCrt)不能使用级联删除,而且你也不能在不先更新 [=14= 的情况下简单地删除 Crt ] 到 null(并调用 SaveChanges)。

无论如何,这是配置所需关系的方法。通常使用 InverseProperty 属性来解决导航 属性 映射歧义就足够了,但是 one-to-one 单向(即导航 属性 仅在其中一端)需要流畅的配置(否则它将按照惯例映射到 one-to-many)。也特别是对于关系,我发现显式流畅配置比考虑所有 EF 常规假设和数据注释要清楚得多,例如 ForeignKey 属性(在 FK 属性 或导航 属性 上),什么字符串放有第一个或后面的情况等等

很快,这里是相关关系的完整显式配置:

// Crt 1 - 0..N CrtChk
modelBuilder.Entity<Crt>()
    .HasMany(e => e.CartChecks)
    .WithOne(e => e.Cart)
    .HasForeignKey(e => e.CrtId)
    .OnDelete(DeleteBehavior.Cascade);

// CrtChk 1 - 0..1 Crt
modelBuilder.Entity<Crt>()
    .HasOne(e => e.ActiveCrtChk)
    .WithOne()
    .HasForeignKey<Crt>(e => e.ActiveCrtChkId)
    .OnDelete(DeleteBehavior.Restrict);

请注意,Cart 属性 不能用于两种关系。首先,因为每个导航 属性 只能映射到一个关系。其次,因为关系模型无法强制 ActiveCrtChkId FK 引用的 CrtChk 记录与引用它的 CrtId 具有相同的 CrtId - 它可以可以是任何其他(尽管从逻辑上讲意图不同)。