我有一个父子关系,其中父关系有一个 ValueObject,我无法确定如何正确定义关系。
为子/父关系添加迁移失败,并显示错误...
实体类型"地址"需要定义主键。
以下是当前的代码结构。
public class Address
{
[Required]
public string BuildingNumber { get; private set; }
// other address properties...
}
public class Parent
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
[Required]
public Address PrimaryAddress { get; private set; }
}
public class ParentContext : DbContext
{
public ParentContext(DbContextOptions<ParentContext> options) :
base(options)
{
}
public DbSet<Parent> Parents { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Parent>().OwnsOne(p => p.PrimaryAddress);
// Flatten the ValueObject fields into table
modelBuilder.Entity<Parent>().OwnsOne(p => p.PrimaryAddress).
Property(b => b.BuildingNumber).IsRequired().
HasColumnName("Primary_BuildingName");
}
}
public class Child
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
[Required]
public int ParentId { get; private set; }
[ForeignKey("ParentId")]
public Parent Parent { get; private set; }
}
public class ChildContext : DbContext
{
public ChildContext(DbContextOptions<ChildContext> options) : base(options)
{
}
public DbSet<Child> Children { get; set; }
}
使用上面的代码示例,我可以运行单独的命令为父级和子级创建迁移,并且表看起来正确。
添加迁移创建父级 -c 父上下文
添加迁移创建子项 -C 子上下文
将关系添加到实体并添加最终迁移将失败。
添加迁移添加父子项-fk -c 子上下文
仅当我在不同的上下文中有孩子和父母时,才会出现问题。
我尝试在父级和子级中以不同的方式定义关系以映射地址字段,以便子级"理解"映射,但我无法避免我尝试过的任何事情的 EF 错误。
示例项目在这里
https://github.com/cimatt55/ef-parent-valueobject
主要问题是单独的上下文。值对象(拥有的实体类型(只是一个结果 - 如果没有值对象,那么您将遇到另一个问题。
您似乎将设计基于一个错误的假设,即只有来自公开DbSet
的实体类。但事实并非如此。还包括导航属性引用的实体,以及它们引用的实体等。
这是合乎逻辑的,因为 EF Core 上下文表示具有表和关系的数据库。EF Core 需要知道所有相关实体,以便正确支持加载相关数据、查询(联接(、级联删除、表、列、主键和外键属性/列及其映射等。
EF Core 文档的"包含和排除类型"部分对此进行了说明:
按照惯例,在上下文的
DbSet
属性中公开的类型包含在模型中。此外,还包括OnModelCreating
方法中提到的类型。最后,通过递归浏览已发现类型的导航属性找到的任何类型也包含在模型中。
调整其示例以ChildContext
,发现以下类型:
Child
因为它在上下文的DbSet
属性中公开Parent
因为它是通过Child.Parent
导航属性发现
的Address
因为它是通过Parent.PrimaryAddress
导航属性发现
的
由于ChildContext
没有Parent
实体配置,EF 假定与Parent
(和Address
(相关的所有内容都是约定,因此例外。
简而言之,使用包含相关实体的单独上下文不是一个好主意。解决方案是在单个上下文中放置和维护所有相关实体。
查看使用的术语,你可能在 DDD 和边界上下文之后,但这些不适合 EF Core(通常适用于关系数据库(模型。