实体框架 - 代码优先:使用相同类型的多个子/可选属性进行映射

Posted

技术标签:

【中文标题】实体框架 - 代码优先:使用相同类型的多个子/可选属性进行映射【英文标题】:Entity Framework - Code First : Mapping with Mutliple child/optional properties of same type 【发布时间】:2011-12-21 10:28:13 【问题描述】:

我的域模型有用户和地址类

public User

   int Id
   Address PrimaryAddress
   Address SecondaryAddress
   and some other Properties

两者都是可选的,但每个地址都必须有一个用户。 Codeirst 映射看起来像

modelBuilder.Entity<User>()
   .HasOptional(u => u.PrimaryAddress)
   .WithRequired()
   .WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
   .HasOptional(u => u.SecondaryAddress)
   .WithRequired()
   .WillCascadeOnDelete(false);

但这并没有产生预期的结果。我期待用户表的两个(int,null)列指向地址表。并且地址表可能有一个 UserId(外键,int,not null)列。 但是用上面的代码地址的“ID”列作为PK和FK(指向用户表)。

【问题讨论】:

【参考方案1】:

您已经映射了一对一的关系,这正是您在数据库中看到的。简而言之,您想要实现的目标是无法通过这种方式实现的。

尤其是Address 必须有User 的部分无法工作,因为User 实体上有两个导航属性,这将导致Address 表中有两个不同的FK 列,否则它们必须可以为空对于某些用户,每个地址都必须是主要地址和次要地址。

正确的模型使用默认映射进行映射(不需要流畅的映射),因为它创建的用户有两个 FK 要寻址。地址与用户存在两个一对多的关系,因为理论上您可以拥有多个具有相同地址的用户。

另一个选项是将关系建模为多对多,其中连接表将被建模为具有附加属性 AddressType 的实体。

如果你真的想遵循你当前的模型,你必须使用这个:

modelBuilder.Entity<User>()
    .HasOptional(u => u.PrimaryAddress)
    .WithOptionalPrincipal()
    .WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
    .HasOptional(u => u.SecondaryAddress)
    .WithOptionalPrincipal()
    .WillCascadeOnDelete(false);

它将在Address 表中创建两个额外的 FK 列,并且关系本身将是一对多的(一个用户可以有多个地址)。原因是强制一对一需要 FK 的唯一性,而当前的 EF 版本不支持唯一键。您可以在后处理中对 FK 列添加唯一约束(通过 custom DB initializer 或使用 EntityFramework.Migrations),但这会导致其他问题。空值在 SQL 中被视为有效值,因此如果您对可空列有唯一约束,则只有单个记录可以具有空值。

【讨论】:

以上是关于实体框架 - 代码优先:使用相同类型的多个子/可选属性进行映射的主要内容,如果未能解决你的问题,请参考以下文章

实体框架代码优先关系 - 如何定义两个对象之间的关系:两个实体之间的可选一对一

实体框架代码优先 - 通过主键将子实体添加到父实体

实体框架 - 多个字段中的相同外键

实体框架代码优先:关系约束中的从属角色和主体角色中的属性数量必须相同

实体框架代码优先 - 不可为空类型的默认值

具有代码优先迁移的实体框架多个项目