【问题标题】:Entity Framework Foreign Key For Circular Relation循环关系的实体框架外键
【发布时间】:2018-09-09 14:58:34
【问题描述】:

我正在将应用程序转换为 Entity Framework Core,但在获取我的两个模型类之间的外键关系时遇到了麻烦。这些类是这样设置的(注意,BaseEntity 上声明了 Guid Id 字段):

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 还存储了活动 CrtChk 记录的Id

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

通过阅读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)也可以是一对多(所有与 Crt 关联的 CrtChks)的关系?

【问题讨论】:

  • “我在这里缺少什么?” - Crt 导航属性不是属性,而是字段 :)
  • @ivanStoev Duh!感谢您指出了这一点!正确设置导航属性后,我收到一个新错误,请参阅我的编辑。
  • 不要让属性为空。

标签: c# entity-framework ef-code-first migration entity-framework-core


【解决方案1】:

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

无论如何,这是您配置所需关系的方式。通常使用InverseProperty 属性来解决导航属性映射的歧义就足够了,但是一对一的单向(即仅在一端具有导航属性)需要流畅的配置(否则它将按照约定映射到一个-太多)。同样特别是对于关系,我发现显式流畅配置比考虑所有 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 - 它可以是任何其他记录(尽管逻辑上意图不同)。

【讨论】:

    猜你喜欢
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    • 2020-07-08
    • 1970-01-01
    • 2018-07-04
    • 1970-01-01
    • 2022-01-04
    • 2018-10-25
    相关资源
    最近更新 更多