如何在不向主体模型添加导航 属性 的情况下在引用模型中添加外键
How can I add Foreign Key in referenced model without adding navigation property to Principal model
我在父模型和子模型之间有一对多的关系。按照项目的约定,我需要创建一个单独的 class 来映射从 EntityTypeConfiguration class.
扩展的每个实体
public class Parent
{
public int Id { get; set; }
[Required]
public string ParentName { get; set; }
//public IEnumerable<Child> Children { get; set; }
}
public class Child
{
[Key]
public int ChildId { get; set; }
[Required]
public string ChildName { get; set; }
[ForeignKey]
public int CustomParentId { get; set; }
public Parent Parent { get; set; }
}
public class ChildMap : EntityTypeConfiguration<Child>
{
}
如何在不向父模型添加子导航 属性 的情况下在 ChildMap 中添加外键?
为清楚起见,将我的评论扩展为答案。 Child 记录可以在没有 parent 集合的情况下包含对其 parent 的引用,并且可以在有或没有 child 中指定的 parent ID 字段的情况下完成:
public class ParentMap : EntityTypeConfiguration<Parent>
{
ToTable("Parents");
HasKey(x => x.Id)
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
//With Parent ID in Child:
public class ChildMap : EntityTypeConfiguration<Child>
{
ToTable("Children");
HasKey(x => x.ChildId)
.Property(x => x.ChildId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Parent)
.WithMany()
.HasForeignKey(x => CustomParentId);
}
// Without Parent ID in child. (Column in table called "CustomParentId")
public class ChildMap : EntityTypeConfiguration<Child>
{
ToTable("Children");
HasKey(x => x.ChildId)
.Property(x => x.ChildId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Parent)
.WithMany()
.Map(x => x.MapKey("CustomParentId"));
}
关于您的实体的一个重要细节:
在您的 Child class 中,Parent 属性 需要标记为虚拟以启用延迟加载。例如:
public class Child
{
// .. Without virtual
public Parent Parent { get; set; }
}
//vs.
public class Child
{
// .. With virtual
public virtual Parent Parent { get; set; }
}
如果使用下面的代码加载一个child...
var child = myContext.Children
.SingleOrDefault(x => x.ChildId == 1);
然后使用...
访问 parent
string parentName = child.Parent?.Name;
在没有虚拟 parent 的情况下,未加载 Parent 引用,因此 Parent 将为 #null。如果 Parent 是虚拟的,那么将进行延迟加载调用以检索 parent 并且您将获得关联的实体。一般来说,延迟加载是有帮助的,但效率不高,因为它可能会导致 round-trip 到数据库。在这两种情况下,如果您这样做了:
var child = myContext.Children
.Include(x => x.Parent)
.SingleOrdefault(x => x.ChildId == 1);
那么 parent 引用将被预先加载和访问,无论是虚拟的还是非虚拟的。
或者,一种更好的检索数据的方法是使用单独的视图模型和 select 您想要从数据和相关数据中获取的字段。例如,使用 returns child 详细信息以及 parent ID 和名称的视图模型:
var child = myContext.Children
.Select(x => new ChildSummaryViewModel
{
ChildId = x.ChildId,
Name = x.Name,
ParentId = x.Parent.Id,
ParentName = x.Parent.Name
})
.SingleOrdefault(x => x.ChildId == 1);
这不需要 parent 上的 .Include(),将从相关的 parent 加载详细信息。如果你有一个相关实体,这似乎不太实用,但如果你处理的是具有许多相关实体的大型实体,这可以大大减少查询时间和触发额外延迟加载查询的风险,并减少从数据库发回的数据大小。 (而不是整个实体,只是您关心的属性。)
更新实体时,您应该使用 .Include() 相关实体来进行调整或改变关系。
我在父模型和子模型之间有一对多的关系。按照项目的约定,我需要创建一个单独的 class 来映射从 EntityTypeConfiguration class.
扩展的每个实体public class Parent
{
public int Id { get; set; }
[Required]
public string ParentName { get; set; }
//public IEnumerable<Child> Children { get; set; }
}
public class Child
{
[Key]
public int ChildId { get; set; }
[Required]
public string ChildName { get; set; }
[ForeignKey]
public int CustomParentId { get; set; }
public Parent Parent { get; set; }
}
public class ChildMap : EntityTypeConfiguration<Child>
{
}
如何在不向父模型添加子导航 属性 的情况下在 ChildMap 中添加外键?
为清楚起见,将我的评论扩展为答案。 Child 记录可以在没有 parent 集合的情况下包含对其 parent 的引用,并且可以在有或没有 child 中指定的 parent ID 字段的情况下完成:
public class ParentMap : EntityTypeConfiguration<Parent>
{
ToTable("Parents");
HasKey(x => x.Id)
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
//With Parent ID in Child:
public class ChildMap : EntityTypeConfiguration<Child>
{
ToTable("Children");
HasKey(x => x.ChildId)
.Property(x => x.ChildId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Parent)
.WithMany()
.HasForeignKey(x => CustomParentId);
}
// Without Parent ID in child. (Column in table called "CustomParentId")
public class ChildMap : EntityTypeConfiguration<Child>
{
ToTable("Children");
HasKey(x => x.ChildId)
.Property(x => x.ChildId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Parent)
.WithMany()
.Map(x => x.MapKey("CustomParentId"));
}
关于您的实体的一个重要细节: 在您的 Child class 中,Parent 属性 需要标记为虚拟以启用延迟加载。例如:
public class Child
{
// .. Without virtual
public Parent Parent { get; set; }
}
//vs.
public class Child
{
// .. With virtual
public virtual Parent Parent { get; set; }
}
如果使用下面的代码加载一个child...
var child = myContext.Children
.SingleOrDefault(x => x.ChildId == 1);
然后使用...
访问 parentstring parentName = child.Parent?.Name;
在没有虚拟 parent 的情况下,未加载 Parent 引用,因此 Parent 将为 #null。如果 Parent 是虚拟的,那么将进行延迟加载调用以检索 parent 并且您将获得关联的实体。一般来说,延迟加载是有帮助的,但效率不高,因为它可能会导致 round-trip 到数据库。在这两种情况下,如果您这样做了:
var child = myContext.Children
.Include(x => x.Parent)
.SingleOrdefault(x => x.ChildId == 1);
那么 parent 引用将被预先加载和访问,无论是虚拟的还是非虚拟的。
或者,一种更好的检索数据的方法是使用单独的视图模型和 select 您想要从数据和相关数据中获取的字段。例如,使用 returns child 详细信息以及 parent ID 和名称的视图模型:
var child = myContext.Children
.Select(x => new ChildSummaryViewModel
{
ChildId = x.ChildId,
Name = x.Name,
ParentId = x.Parent.Id,
ParentName = x.Parent.Name
})
.SingleOrdefault(x => x.ChildId == 1);
这不需要 parent 上的 .Include(),将从相关的 parent 加载详细信息。如果你有一个相关实体,这似乎不太实用,但如果你处理的是具有许多相关实体的大型实体,这可以大大减少查询时间和触发额外延迟加载查询的风险,并减少从数据库发回的数据大小。 (而不是整个实体,只是您关心的属性。)
更新实体时,您应该使用 .Include() 相关实体来进行调整或改变关系。