如何与实体框架建立一对一或零的关系,并在子实体上公开外键

本文关键字:实体 框架 建立 一对一 关系 | 更新日期: 2023-09-27 18:05:04

我有一个一对一关系的工作配置,看起来像这样:

public class ParentEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; } 
}
public class ChildEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    [Required]
    public Guid ParentId { get; set; }
    [ForeignKey("ParentId")]
    public ParentEntity Parent { get; set; }
}

这个解决方案已经在生产中并且工作得很好。现在,我需要能够从父实体引用子实体,所以我尝试这样做:

public class ParentEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    public virtual ChildEntity Child { get; set; }
}

和我收到这个错误:

System.InvalidOperationExceptionUnable to determine the principal end of an association between the types 'ChildEntity' and 'ParentEntity'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

我遇到了多种解决方案,如何配置原则与Fluent API,但没有什么能让我在ParentEntity上添加ChildEntity参考,并在ChildEntity上维护ParentId

如何与实体框架建立一对一或零的关系,并在子实体上公开外键

在第一个解决方案中,EF创建了一个单向的一对多关系。为了帮助您更好地理解正在发生的事情,使用Fluent Api配置该关系将是:

modelBuilder.Entity<ChildEntity>()
            .HasRequired(ce=>ce.Parent)
            .WithMany()
            .HasForeignKey(ce=>ce.ParentId);

现在,当您添加ChildEntity导航属性时,EF知道您想要创建一个一对一的关系。在一对一的关系中,EF要求依赖实体的PK也是关系的FK:

public class ParentEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; } 
    public virtual ChildEntity Child { get; set; }
}
public class ChildEntity
{
    [Key, ForeignKey("Parent")]
    public Guid Id { get; set; }
    public ParentEntity Parent { get; set; }
}

但我不认为这是你正在寻找的模型,因为一个ParentEntity可能是一个以上的ChildEntity的父母,所以在我看来,你需要配置一个一对多的关系:

public class ParentEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; } 
    public virtual ICollection<ChildEntity> Children { get; set; }
}
public class ChildEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    [ForeignKey("Parent")]
    public Guid ParentId { get; set; }
    public ParentEntity Parent { get; set; }
}

更新

如果你想创建一个一对一的关系,并且两个实体都有自己的pk,那么由于我在回答开头解释的一对一关系的限制,你不能在依赖实体中定义FK属性。您所需要的解决方案可以使用如下的模型:

public class ParentEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; } 
    public virtual ChildEntity Child { get; set; }
}
public class ChildEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    public ParentEntity Parent { get; set; }
}

将这个Fluent Api配置添加到您的上下文中,以指定谁是您关系中的主体和依赖对象,并指定您已经在DB上拥有的FK的名称(您不能在您的实体上拥有具有该名称的属性):

modelBuilder.Entity<ChildEntity>()
            .HasRequired(sr => sr.Parent)
            .WithOptional(s => s.Child)
            .Map(c=>c.MapKey("Parent_Id"));