Entity Framework 6 FluentApi One-to-One 关系配置
Entity Framework 6 FluentApi One-to-One Relationship configuration
[Table("Child", Schema = "dbo")]
public partial class Child
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public int? Parent1Id { get; set; }
public int? Parent2Id { get; set; }
public virtual Parent Parent1 {get; set}
public virtual Parent Parent2 {get; set}
}
[Table("Parent", Schema = "dbo")]
public partial class Parent
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public virtual Child Child1 { get; set; }
public virtual Child Child2 { get; set; }
//modelBuilder below would work if I had this
//public virtual ICollection<Child> Child1 { get; set; }
}
这意味着one-to-one之间的关系Parent-Child。
Parent1Id 和 Parent2Id 可以为空。
我只找到了 one-to-many 关系的示例(使用 FluentAPI),如果我在 Parent 中有一个 Child 的集合,我必须做类似的事情:
modelBuilder.Entity<Parent>()
.HasMany(e => e.Child1)
.WithOptional(e => e.Parent1)
.HasForeignKey(e => e.Parent1Id);
modelBuilder.Entity<Parent>()
.HasMany(e => e.Child2)
.WithOptional(e => e.Parent2)
.HasForeignKey(e => e.Parent2Id);
Child 将有 2 个 FK 引用 Parent。
Parent 没有 FK 引用 child。
我的问题是,如何使用 EF 6.x 作为 one-to-one 关系执行此操作?
我看到了 HasOne() 方法,但它来自 EFCore,所以我在这个方法中没有选择。
谢谢
注意:从技术上讲,一对一关系在 MS SQL 服务器中是不可能的。这些将始终是一对零或一的关系。 EF 在不在数据库中的实体上形成一对一关系。
parent 是主体,child 是从属,因此 child 需要 parent 但 child 是可选的parent:
modelBuilder.Entity<Child>()
.HasRequired(c => c.Parent1)
.WithOptional(p => p.Child1)
.HasForeignKey(c => c.Parent1Id);
通常,您会使用受抚养人的 PK 作为主要实体的 FK(这是确保 one-to-one 关系的原因),但您在这里尝试一些不寻常的事情 - 您似乎想要 one-to-two关系。我的意见是重新考虑您的设计 - 将其设为 1:N 并限制 application/DbContext 代码中的依赖项数量,或者从 Child
中派生出两种不同的类型,以便它们是 [=17] 中的不同实体=] 模型,然后使用标准的“PK 作为 FK”one-to-one 映射。
EF6 使用所谓的 共享主键关联 自然地仅支持一对一关系,其中从属 PK 也用作 FK。但是,它们不适用于这种情况(当您需要两个实体之间存在多个一对一关系时)。所以你需要基于一对一 FK 的关系。
它们受支持,但有以下限制:
- EF6 不会在数据库中强制执行“to 1”(或“to 0 or 1”)所需的 FK 列上创建唯一 constraint/index,因此从数据库的角度来看它们将是一对多。
- 不支持显式 FK 属性(注意在配置一对一关系时缺少
HasForeignKey
流畅 API)。您必须删除它们并仅使用导航属性。 FK 列 名称可以在 Map
方法中使用 MapKey
指定。
您必须接受这些限制,否则您无法在此类模型上使用 EF6。
首先从模型中删除显式 FK 属性:
// remove these:
//public int? Parent1Id { get; set; }
//
//public int? Parent2Id { get; set; }
接下来要考虑的是 EF 关系始终认为一方是主要的,另一方是从属的。在一对多中,“一”方始终是主体,“多”方始终是从属。对于一对一,通常 required end 是 principal,optional end 是 dependent,但是当两端都需要或者 both end是可选的(如您的情况),您必须通过使用正确的 WithRequired
/ WithOptional
方法来指定,其中 Principal
/ Dependent
后缀指定如何处理实体正在配置(Has
方法的通用类型参数)。这很重要,但通常会使人感到困惑,因为 Map
中的其余流畅配置始终适用于依赖实体,无论使用哪个实体启动配置。
话虽这么说,在您的样本中有两种关系,两者都以 Parent
为主,Child
为从属,两端都是可选的。因此,流畅的配置可以是
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child1)
.WithOptionalPrincipal(c => c.Parent1)
.Map(c => c.MapKey("Parent1Id"));
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child2)
.WithOptionalPrincipal(c => c.Parent2)
.Map(c => c.MapKey("Parent2Id"));
也可以用其他方式配置:
modelBuilder.Entity<Child>()
.HasOptional(c => c.Parent1)
.WithOptionalDependent(p => p.Child1)
.Map(c => c.MapKey("Parent1Id"));
modelBuilder.Entity<Child>()
.HasOptional(c => c.Parent2)
.WithOptionalDependent(p => p.Child2)
.Map(c => c.MapKey("Parent2Id"));
注意:使用一个或另一个,而不是两个。通常关系配置从有导航属性的一端开始(因为Has
方法需要),但是当两端都有导航时,这只是个人的问题preference/taste。
[Table("Child", Schema = "dbo")]
public partial class Child
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public int? Parent1Id { get; set; }
public int? Parent2Id { get; set; }
public virtual Parent Parent1 {get; set}
public virtual Parent Parent2 {get; set}
}
[Table("Parent", Schema = "dbo")]
public partial class Parent
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public virtual Child Child1 { get; set; }
public virtual Child Child2 { get; set; }
//modelBuilder below would work if I had this
//public virtual ICollection<Child> Child1 { get; set; }
}
这意味着one-to-one之间的关系Parent-Child。 Parent1Id 和 Parent2Id 可以为空。
我只找到了 one-to-many 关系的示例(使用 FluentAPI),如果我在 Parent 中有一个 Child 的集合,我必须做类似的事情:
modelBuilder.Entity<Parent>()
.HasMany(e => e.Child1)
.WithOptional(e => e.Parent1)
.HasForeignKey(e => e.Parent1Id);
modelBuilder.Entity<Parent>()
.HasMany(e => e.Child2)
.WithOptional(e => e.Parent2)
.HasForeignKey(e => e.Parent2Id);
Child 将有 2 个 FK 引用 Parent。 Parent 没有 FK 引用 child。
我的问题是,如何使用 EF 6.x 作为 one-to-one 关系执行此操作? 我看到了 HasOne() 方法,但它来自 EFCore,所以我在这个方法中没有选择。
谢谢
注意:从技术上讲,一对一关系在 MS SQL 服务器中是不可能的。这些将始终是一对零或一的关系。 EF 在不在数据库中的实体上形成一对一关系。
parent 是主体,child 是从属,因此 child 需要 parent 但 child 是可选的parent:
modelBuilder.Entity<Child>()
.HasRequired(c => c.Parent1)
.WithOptional(p => p.Child1)
.HasForeignKey(c => c.Parent1Id);
通常,您会使用受抚养人的 PK 作为主要实体的 FK(这是确保 one-to-one 关系的原因),但您在这里尝试一些不寻常的事情 - 您似乎想要 one-to-two关系。我的意见是重新考虑您的设计 - 将其设为 1:N 并限制 application/DbContext 代码中的依赖项数量,或者从 Child
中派生出两种不同的类型,以便它们是 [=17] 中的不同实体=] 模型,然后使用标准的“PK 作为 FK”one-to-one 映射。
EF6 使用所谓的 共享主键关联 自然地仅支持一对一关系,其中从属 PK 也用作 FK。但是,它们不适用于这种情况(当您需要两个实体之间存在多个一对一关系时)。所以你需要基于一对一 FK 的关系。
它们受支持,但有以下限制:
- EF6 不会在数据库中强制执行“to 1”(或“to 0 or 1”)所需的 FK 列上创建唯一 constraint/index,因此从数据库的角度来看它们将是一对多。
- 不支持显式 FK 属性(注意在配置一对一关系时缺少
HasForeignKey
流畅 API)。您必须删除它们并仅使用导航属性。 FK 列 名称可以在Map
方法中使用MapKey
指定。
您必须接受这些限制,否则您无法在此类模型上使用 EF6。
首先从模型中删除显式 FK 属性:
// remove these:
//public int? Parent1Id { get; set; }
//
//public int? Parent2Id { get; set; }
接下来要考虑的是 EF 关系始终认为一方是主要的,另一方是从属的。在一对多中,“一”方始终是主体,“多”方始终是从属。对于一对一,通常 required end 是 principal,optional end 是 dependent,但是当两端都需要或者 both end是可选的(如您的情况),您必须通过使用正确的 WithRequired
/ WithOptional
方法来指定,其中 Principal
/ Dependent
后缀指定如何处理实体正在配置(Has
方法的通用类型参数)。这很重要,但通常会使人感到困惑,因为 Map
中的其余流畅配置始终适用于依赖实体,无论使用哪个实体启动配置。
话虽这么说,在您的样本中有两种关系,两者都以 Parent
为主,Child
为从属,两端都是可选的。因此,流畅的配置可以是
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child1)
.WithOptionalPrincipal(c => c.Parent1)
.Map(c => c.MapKey("Parent1Id"));
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child2)
.WithOptionalPrincipal(c => c.Parent2)
.Map(c => c.MapKey("Parent2Id"));
也可以用其他方式配置:
modelBuilder.Entity<Child>()
.HasOptional(c => c.Parent1)
.WithOptionalDependent(p => p.Child1)
.Map(c => c.MapKey("Parent1Id"));
modelBuilder.Entity<Child>()
.HasOptional(c => c.Parent2)
.WithOptionalDependent(p => p.Child2)
.Map(c => c.MapKey("Parent2Id"));
注意:使用一个或另一个,而不是两个。通常关系配置从有导航属性的一端开始(因为Has
方法需要),但是当两端都有导航时,这只是个人的问题preference/taste。