使用 EF7 映射一对零或一

Posted

技术标签:

【中文标题】使用 EF7 映射一对零或一【英文标题】:Mapping One-to-Zero-or-One with EF7 【发布时间】:2016-07-30 05:27:32 【问题描述】:

我目前正在清理一个相当大的数据库。部分数据库具有一对零或一映射的关系。具体来说:

User -> UserSettings

并非所有用户都有用户设置,但没有用户就不能存在用户设置。不幸的是,这些表已经存在。 User 有一个 PK ID。 UserSettings 有一个 PK ID 和一个列 User_Id_Fk,此时它不是真正的 FK(没有定义关系)。

我正在修复这个问题,并且已经通过 SQL 从数据库的角度进行了修复,并通过测试进行了确认。 (添加了 FK 约束。在 User_Id_Fk 上添加了唯一约束。)这一切都是在 UserSettings 表上完成的。 (注意:我这里没有使用EF Migrations,此时我必须手动编写SQL。)

但是,我现在需要连接现有应用程序以正确处理这个新映射。该应用程序使用 ASP.NET Core 1.0 和 EF7。以下是现有数据模型的(缩短)版本。

public class User

  public int Id  get; set; 

  public virtual UserSettings UserSettings  get; set; 


public class UserSettings

  public int Id  get; set; 

  [Column("User_Id_Fk")]
  public int UserId  get; set; 

  [ForeignKey("UserId")]
  public virtual User User  get; set; 

我也有这个 Fluent Mapping:

builder.Entity<UserSettings>()
            .HasOne(us => us.User)
            .WithOne(u => u.User)
            .IsRequired(false);

当我运行应用程序并访问数据库中的这些项目时,我收到此错误,然后是一组神秘的消息,其中没有与我的应用程序直接相关的信息。:

ArgumentNullException: Value cannot be null.
Parameter name: navigation
Microsoft.Data.Entity.Utilities.Check.NotNull[T] (Microsoft.Data.Entity.Utilities.T value, System.String parameterName) <0x10d28a650 + 0x00081> in <filename unknown>, line 0

经过研究,有人提到UserSettings类的ID必须和外键一样,像这样:

public class UserSettings

  [Key, ForeignKey("User")]
  public int Id  get; set; 

  public virtual User User  get; set; 

我真的没有这个选项,因为数据库正在用于我目前无法控制的其他应用程序。那么,我被困在这里了吗?我是否只需要维护一个 1:many 映射(现在可能发生,尽管它还没有)并且没有适当的 1:0..1 映射约束?

更新 看看下面 octavioccl 的答案,我尝试了一下,但没有成功。但是,我随后从UserSettings 的映射中删除了User(但我离开了UserId)。据我所知,一切似乎都有效。然而,我真的很困惑这里发生了什么,如果这甚至是正确的答案,或者我只是走运。

【问题讨论】:

【参考方案1】:

删除数据注释并尝试以下配置:

builder.Entity<UserSettings>()
            .Property(b => b.UserId)
            .HasColumnName("User_Id_Fk");

builder.Entity<User>()
            .HasOne(us => us.UserSettings)
            .WithOne(u => u.User)
            .HasForeignKey<UserSettings>(b => b.UserId);

来自EF Core documentation:

配置外键时需要指定依赖 实体类型 - 注意提供给HasForeignKey 的通用参数 在上面的清单中。在一对多的关系中,很明显 具有参考导航的实体是依赖的,也是唯一的 以收藏为主。但事实并非如此 一对一的关系 - 因此需要明确定义它。

quoted link (Blog-BlogImage) 中提供的示例与您想要实现的目标几乎相同。

如果我上面显示的解决方案不起作用,那么您应该检查User_Id_Fk 列是否允许null。如果是这种情况,请将 FK 属性类型更改为 int?

public class UserSettings

  public int Id  get; set; 

  public int? UserId  get; set; 

  public virtual User User  get; set; 

【讨论】:

感谢您的回复。不幸的是,没有去。我对您的回答感到有些困惑,即用户应该只有零个或一个 UserSetting。我不明白您的解决方案如何实现这一目标。 (这超出了我仍然收到相同错误的事实。)您能否提供澄清?谢谢! 我现在看到了您的更新,我认为它有效,因为 EF 现在将您的关系视为一对多而不是一对一。当您在相关实体之间仅声明一个导航属性时,EF 默认创建一对多,我也对您当前模型的工作方式感到困惑,因为如果您在UserSettings 表中有 FK 列,以防 EF 看到一对多,集合导航属性应在User 中声明,而不是引用导航属性。确实很迷茫,让我再想一想,如果我找到合理的东西我会告诉你 是的,我同意这种安排有点奇怪。最后,User 应该只有一个或没有UserSettings。每个UserSettings 都应该附加到一个用户。 (这是通过对UserSettings 的 FK 的唯一约束来强制执行的。)我不确定如何在 EF 中正确建模这种关系。感谢您的帮助。 哈哈,所以,我刚刚从你那里找到了这个答案(原则/依赖)。它成功了(我认为)。我不认为我想做的事情是可能的,否则。 ***.com/questions/28499126/…

以上是关于使用 EF7 映射一对零或一的主要内容,如果未能解决你的问题,请参考以下文章

一对零或一关系实体框架

EF 代码优先 - 配置一对零或一关系,无需共享 PK/FK

Fluent API 首先在 EF 代码中实现零或一到零或一的关系

Oracle SQL - 将选择计数(*)转换为零或一

实体代码第一个零或一到零或一关系

使用 CodeFirst 的 C# 一对零或一关系