EF CF - 如何建立 optional:optional 关系?
EF CF - How do I build optional:optional relationship?
我希望 Foo
有一个可选的 Bar
和 Bar
有一个可选的 Foo
。
我似乎设法让它工作,但我只在其中一个表上创建了一个额外的列,例如它只在其中一张表上 InvitationId
然后 Invitation_Id
in SQL,即使两个实体的设置方式相同,但相反。
所以我想做一个更小的复现,这样我就可以在 SO 上提问,但是在这个过程中,虽然我刚刚复制了原始实体,删除了一些属性,但我现在有一个不同的错误,这是令人担忧的不确定性。
好的,代码。
[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);
错误是:
The navigation property 'Foo' declared on type 'Product.Data.Entities.Bar' has been configured with conflicting foreign keys.
原始实体仍然存在于项目中,并且以完全相同的方式设置,除了它们具有更多属性,但它们的创建没有错误,除了一个具有无关的 FK 列。
所以有很多问题:
为什么我得到额外的 Invitation_Id
列,而它已经有 InvitationId
?
为什么我重现不了?
为什么会出现这个错误?如果我解决了这个问题,如果我的原始实体没有同样的问题,它会帮助我吗?
在我上面的开头句中实现我的 objective 的正确方法是什么?
与此同时,我将开始一点一点地将 Foo
和 Bar
构建回 Invitation
和 Expectation
,直到变得有趣为止。
更新
所以我最终得到了除了名称之外的所有原始实体的精确副本。这些副本导致了上面的FK冲突错误,但原件没有!
然后我删除了原件并将副本重命名为原来的名称,更改了 none 属性或属性,错误消失了,我又回到了无关 FK 列的原始问题!
疯狂。
卢克
正如我在评论中提到的那样,因为存在两种关系,并且可以为关系的每一方都有一个导航 属性 我认为 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);
首先,在一对一关系中,一端必须是主体,另一端是从属,因此您不能在两个实体中指定 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 显示的解决方案,但在那种情况下,您将拥有两种不同的关系。
我希望 Foo
有一个可选的 Bar
和 Bar
有一个可选的 Foo
。
我似乎设法让它工作,但我只在其中一个表上创建了一个额外的列,例如它只在其中一张表上 InvitationId
然后 Invitation_Id
in SQL,即使两个实体的设置方式相同,但相反。
所以我想做一个更小的复现,这样我就可以在 SO 上提问,但是在这个过程中,虽然我刚刚复制了原始实体,删除了一些属性,但我现在有一个不同的错误,这是令人担忧的不确定性。
好的,代码。
[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);
错误是:
The navigation property 'Foo' declared on type 'Product.Data.Entities.Bar' has been configured with conflicting foreign keys.
原始实体仍然存在于项目中,并且以完全相同的方式设置,除了它们具有更多属性,但它们的创建没有错误,除了一个具有无关的 FK 列。
所以有很多问题:
为什么我得到额外的
Invitation_Id
列,而它已经有InvitationId
?为什么我重现不了?
为什么会出现这个错误?如果我解决了这个问题,如果我的原始实体没有同样的问题,它会帮助我吗?
在我上面的开头句中实现我的 objective 的正确方法是什么?
与此同时,我将开始一点一点地将 Foo
和 Bar
构建回 Invitation
和 Expectation
,直到变得有趣为止。
更新
所以我最终得到了除了名称之外的所有原始实体的精确副本。这些副本导致了上面的FK冲突错误,但原件没有!
然后我删除了原件并将副本重命名为原来的名称,更改了 none 属性或属性,错误消失了,我又回到了无关 FK 列的原始问题!
疯狂。
卢克
正如我在评论中提到的那样,因为存在两种关系,并且可以为关系的每一方都有一个导航 属性 我认为 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);
首先,在一对一关系中,一端必须是主体,另一端是从属,因此您不能在两个实体中指定 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 显示的解决方案,但在那种情况下,您将拥有两种不同的关系。