一对一或零对一实体框架代码优先 Fluent Api

Posted

技术标签:

【中文标题】一对一或零对一实体框架代码优先 Fluent Api【英文标题】:One or Zero to One Entity Framework Code First FluentApi 【发布时间】:2015-04-14 11:12:30 【问题描述】:
    我需要创建 fluentapi 一对一或零对一引用,并在两个实体上都具有导航属性。

    EntityTwo 应该包含简单的属性来存储外键 (EntityOneId)

    public class EntityOne
    
        public int Id  get; set; 
        public EntityTwo EntityTwo  get; set; 
    
    
    public class EntityTwo
    
        public int Id  get; set; 
        public int EntityOneId  get; set; 
        public EntityOne EntityOne  get; set; 
    
    
    public class MyDbContext : DbContext
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        
            base.OnModelCreating(modelBuilder);
            //some code trimmed
    
            modelBuilder.Entity<EntityOne>()
                .HasOptional(entity => entity.EntityTwo)
                .WithRequired();
    
            modelBuilder.Entity<EntityTwo>()
                .HasRequired(entity => entity.EntityOne)
                .WithMany()
                .HasForeignKey(entity => entity.EntityOneId)
                .WillCascadeOnDelete(false);
        
    
    

更复杂的场景:

public class EntityOne

    public int Id  get; set; 

    public EntityTwo EntityTwo  get; set; 


public class EntityThree

    public int Id  get; set; 

    public EntityTwo EntityTwo  get; set; 


public class EntityTwo

    public int Id  get; set; 

    public int EntityOneId  get; set; 

    public EntityOne EntityOne  get; set; 

    public int EntityThreeId  get; set; 

    public EntityThree EntityThree  get; set; 


public class MyDbContext : DbContext

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        base.OnModelCreating(modelBuilder);

        //some code trimmed

        modelBuilder.Entity<EntityOne>()
            .HasOptional(entity => entity.EntityTwo)
            .WithRequired();

        modelBuilder.Entity<EntityThree>()
            .HasOptional(entity => entity.EntityTwo)
            .WithRequired();

        modelBuilder.Entity<EntityTwo>()
            .HasRequired(entity => entity.EntityOne)
            .WithMany()
            .HasForeignKey(entity => entity.EntityOneId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<EntityTwo>()
            .HasRequired(entity => entity.EntityThree)
            .WithMany()
            .HasForeignKey(entity => entity.EntityThreeId)
            .WillCascadeOnDelete(false);
    

【问题讨论】:

可能与this 重复。还是我错了? 为清楚起见,您可以使用***.com/a/45182785/1941942 【参考方案1】:

在一对一关系中,一端必须是主要的,而另一端必须是从属的。主端是最先插入的端,它可以在没有依赖端的情况下存在。依赖端是必须插入主体之后的端,因为它具有主体的外键。配置一对一关系时,Entity Framework 要求依赖的主键也是外键。实现您想要的正确方法可能是这样,但使用数据注释:

public class EntityOne

  public int Id  get; set; 
  public virtual EntityTwo EntityTwo  get; set; 


 public class EntityTwo
 
   [Key, ForeignKey("EntityOne")]
   public int EntityOneId  get; set; 
   public virtual EntityOne EntityOne  get; set; 

我建议您查看此link,您可以在此处找到有关如何在 EF Code First 中工作的更多信息。

更新:

恐怕您想要的东西是不可能的。您不能与未声明为 PK 的 FK 创建一对一的关系。如果您想让每个实体都有自己的Id 并配置这两个实体之间的一对一关系,请删除EntityTwo 中的 FK 属性。

我的建议是使用 Fluent Api 映射该关系,如下所示:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

    modelBuilder.Entity<EntityTwo>()
        .HasRequired(et => et.EntityOne)
        .WithOptional(eo=>eo.EntityTwo);

或者您可以在主要的导航属性上添加Required 属性,例如:

public class EntityTwo

  public int Id  get; set; 
  // public int EntityOneId  get; set; 
  [Required]
  public EntityOne EntityOne  get; set; 

【讨论】:

这个例子是有效的,但是它不符合我的期望。我希望 EntityTwo 有自己的 Id,并带有指向其他实体的链接(每个链接都是唯一的) 你好@OlegGochachko。我前段时间更新了我的答案。您是否尝试过该配置? “您不能与未声明为 PK 的 FK 创建一对一关系。”通过 SQL,FK 是候选键之一就足够了。【参考方案2】:

我想出的唯一方法是创建一个集合和一个辅助属性来表示一/零侧,这确实有点难看。为了清楚起见,包括数据注释。

public class EntityOne

    [Key]
    public int EntityOneId  get; set; 

    public EntityTwo EntityTwo => EntityTwoNavigation?.FirstOrDefault();
    public ICollection<EntityTwo> EntityTwoNavigation  get; set; 


public class EntityTwo

    [Key]
    public int EntityTwoId  get; set; 
    public int EntityOneId  get; set; 

    [ForeignKey("EntityOneId")]
    public EntityOne EntityOne  get; set; 

【讨论】:

以上是关于一对一或零对一实体框架代码优先 Fluent Api的主要内容,如果未能解决你的问题,请参考以下文章

使用实体框架 4.1 代码优先方法将一对一的表关系映射到单个实体

用于一对一识别关系的实体框架代码优先流利 API 配置

实体框架代码优先关系 - 如何定义两个对象之间的关系:两个实体之间的可选一对一

实体框架 4.1 代码优先外键 ID

实体框架代码优先 - Fluent Api 与数据注释的优缺点 [关闭]

实体框架中与代码优先外键的更改冲突