EF CF - 如何构建可选:可选关系?

时间:2015-07-16 12:37:23

标签: entity-framework ef-code-first

我希望Foo有一个可选的BarBar来选择Foo

我似乎设法让它工作但我只在其中一个表上创建了一个额外的列,例如它在SQL中仅在其中一个表上创建InvitationId然后Invitation_Id,即使两个实体的设置方式相同,但反之亦然。

Screenshot showing extra Id column

所以我想做一个较小的repro所以我可以在SO上问这个问题,但在这个过程中,虽然我刚刚复制了原始实体,删除了一些属性,但我现在有一个不同的错误,这令人担忧-deterministic。

好的,代码。

[Table("Foo")]
public partial class Foo
{
    [Key]
    public Guid Id { get; set; }

    [Required]
    [StringLength(128)]
    public string Name { get; set; }

    // Referential

    [ForeignKey("Bar")]
    public Guid? BarId { get; set; }

    public virtual Bar Bar { get; set; }
}

[Table("Bar")]
public partial class Bar
{
    [Key]
    public Guid Id { get; set; }

    [Required]
    [StringLength(128)]
    public string Name { get; set; }

    // Referential

    [ForeignKey("Foo")]
    public Guid? FooId { get; set; }

    public virtual Foo Foo { get; set; }
}

并在OnModelCreating

    modelBuilder.Entity<Foo>()
        .HasOptional<Bar>(foo => foo.Bar)
        .WithOptionalPrincipal(bar => bar.Foo);

错误是:

  

在“Product.Data.Entities.Bar”类型上声明的导航属性“Foo”已配置了冲突的外键。

原始实体仍然存在于项目中,并且以完全相同的方式设置,除了它们具有更多属性,但它们创建时没有错误,除了一个具有无关的FK列。

所以有很多问题:

  • 为什么我已经获得Invitation_Id列的额外InvitationId列?

  • 为什么我不能重现它?

  • 为什么现在出现错误?如果我解决了这个问题,如果他们没有相同的问题,是否可以帮助我解决原始实体。

  • 在上面的开头句中实现目标的正确方法是什么?

与此同时,我将开始逐步将FooBar建设成InvitationExpectation,直到它变得有趣。

更新

所以我最终得到了除了名字之外的所有原始实体的精确副本。这些副本导致上面的FK冲突错误,但原件没有!!

然后我删除了原件并将副本重命名为原始名称,不更改任何属性或属性,错误消失了,我又回到了无关FK列的原始版本!

疯狂。

2 个答案:

答案 0 :(得分:2)

第一件事是一对一关系,一端必须是主体,另一端是依赖关系,所以你不能在两个实体中指定FK属性。第二件事是如果你打算使用FK属性,EF要求依赖实体的PK也应该是FK:

public class Principal
{
   public int Id{get;set;}
   public virtual Dependent Dependent{get;set;}
}

public class Dependent
{
   [Key, ForeignKey("Principal")]
   public int PrincipalId{get;set;}

   public virtual Principal Principal{get;set;}
}

第三件事是EF允许你使用Fluent API配置双方可选的一对一关系,但你可以指定FK,因为正如我之前所说,它也应该配置为PK,所以EF将在DB中为您处理该FK,这样您就可以获得额外的Invitation_Id列。

要解决您的问题,您的模型应该是这样的(删除FK属性):

[Table("Foo")]
public partial class Foo
{
    [Key]
    public Guid Id { get; set; }

    [Required]
    [StringLength(128)]
    public string Name { get; set; }

    // Referential
    public virtual Bar Bar { get; set; }
}

[Table("Bar")]
public partial class Bar
{
    [Key]
    public Guid Id { get; set; }

    [Required]
    [StringLength(128)]
    public string Name { get; set; }

    // Referential
    public virtual Foo Foo { get; set; }
}

使用相同的Fluent Api配置:

 modelBuilder.Entity<Foo>()
        .HasOptional(foo => foo.Bar)
        .WithOptionalPrincipal(bar => bar.Foo);

关于为什么在您的真实代码中没有发生异常,我认为与@ user2697817相同,您应该创建两个不同的关系,但我可以完全确保,因为我没有看到您的真实模型。

第二个选项可能是@ user2697817显示的解决方案,但在这种情况下,您将有两种不同的关系。

答案 1 :(得分:1)

正如我在评论中提到的,因为有两种关系,并且可以为关系的每一方都有一个导航属性,我认为EF无法区分哪个导航道具是哪种关系的一部分。登记/> 我建议您在OnModelCreating

中明确定义这两种关系
modelBuilder.Entity<Foo>().HasOptional(f => f.Bar)
                          .WithMany()
                          .HasForeignKey(f => f.BarId);

modelBuilder.Entity<Bar>().HasOptional(b => b.Foo)
                          .WithMany()
                          .HasForeignKey(b => b.FooId);