【问题标题】:Entity Framework Code First One-to-One Required-Required RelationshipEntity Framework Code First 一对一的必需-必需关系
【发布时间】:2021-01-16 06:15:45
【问题描述】:

使用 Entity Framework Code First 4.3.1 时,可以创建具有 1 对 1 多重性的关系。也就是说,关系的每一端都有一个实体。

可以将 1 对 1 关系配置为 required-requiredrequired-optional ^。但是,当我在两者之间切换时,我看不出有任何区别:

  • 生成的数据库架构。我的目标是 SQL Server 2008。
  • EF 的运行时行为。

因此,尽管关系被配置为 required-required .这似乎与 HasRequired(...) 的文档相矛盾:

从该实体类型配置所需的关系。除非指定此关系,否则实体类型的实例将无法保存到数据库中。数据库中的外键不可为空。

http://msdn.microsoft.com/en-us/library/gg671317

必需-必需关系实体:

public class RequiredPrincipalA
{
    public int Id { get; set; }
    public virtual RequiredDependentA DependentA { get; set; }
}

public class RequiredDependentA
{
    public int Id { get; set; }
    public virtual RequiredPrincipalA PrincipalA { get; set; }
}

必选关系实体:

public class RequiredPrincipalB
{
    public int Id { get; set; }
    public virtual OptionalDependentB DependentB { get; set; }
}

public class OptionalDependentB
{
    public int Id { get; set; }
    public virtual RequiredPrincipalB PrincipalB { get; set; }
}

DbContext 和模型配置:

public class AppContext : DbContext
{
    public DbSet<RequiredPrincipalA> PrincipalAs { get; set; }
    public DbSet<RequiredDependentA> DependentAs { get; set; }

    public DbSet<RequiredPrincipalB> PrincipalBs { get; set; }
    public DbSet<OptionalDependentB> DependentBs { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<RequiredPrincipalA>()
            .HasRequired(o => o.DependentA)
            .WithRequiredPrincipal(o => o.PrincipalA);

        modelBuilder.Entity<RequiredPrincipalB>()
            .HasOptional(o => o.DependentB)
            .WithRequired(o => o.PrincipalB);
    }
}

测试代码:

Database.SetInitializer(new DropCreateDatabaseAlways<AppContext>());

using (var ctx = new AppContext())
{
    ctx.Database.Initialize(force: false);

    ctx.PrincipalAs.Add(new RequiredPrincipalA());
    ctx.PrincipalBs.Add(new RequiredPrincipalB());

    ctx.SaveChanges();
}

我知道我可以将 [Required] 数据属性添加到 RequiredPrincipalA.DependentARequiredDependentA.PrincipalA 的导航属性中。这将导致 EF 验证阻止上述情况。但是,我不想这样做,因为它还会在更新现有实体时验证导航属性是否已填充。这意味着应用程序必须为每次更新预取关系另一端的实体。

为什么仅在更改 required-requiredrequired-optional 之间的关系时,我看不到 EF 的行为有任何差异?

^ 请注意,也支持可选可选,但这不构成我的问题的一部分。配置可选-可选关系时,生成的数据库架构和运行时行为存在明显差异。

【问题讨论】:

    标签: entity-framework


    【解决方案1】:

    我不知道为什么在这种情况下允许 required-required 但它不能存在于数据库中,因为关系是建立在主键上的。 required-required 意味着如果相关B不存在则不能插入A,如果相关A不存在则不能插入B => A或B都不能插入。

    数据库关系始终具有主体和依赖实体 - 主体可以始终存在而没有依赖。

    只有当 A 和 B 都映射到同一个表 (table splitting) 时,才能在 EF 中实现真正的 required-required,因为在这种情况下,它们都是使用单个插入命令插入的。

    【讨论】:

    • 感谢您的回答。它肯定澄清了我的问题的数据库模式方面。但是,我非常想知道为什么 EF 没有任何明显的行为变化。
    • 是否允许 required-required 因为它可以在运行时由 EF 强制执行,即使实际上不可能在 SQL 中强制执行此约束?
    • 卢克,由于 Ladislav 的回答,现在可以理解配置 r/r 和 r/o 时架构没有区别的事实。现在的重点是 r/r 似乎并没有在运行时由 EF 强制执行,尽管文档说明了。
    • 这可能只是文档中的一个错误。这不是第一次发生。
    • 可能,尽管这意味着将 1:1 关系配置为 r/r 与 r/o 之间没有实际区别。如果是这种情况,我看不出在 API 中有选项有什么意义。
    【解决方案2】:

    这不是一个真正的答案,但我要说的比 cmets 还要多。但你知道,我写了 900 页的书……这就是我滚动的方式。 :)

    奇怪的是,我希望流畅的配置与数据注释的行为方式相同,但我很困惑它没有这样做。 (我已经用指向该线程的链接 ping Rowan Miller 以获得他的反馈。)我的意思是:在 SaveChanges 期间验证约束。

    在数据库方面,我使用 Ladislav。在模型中,EF 使用相关实体的键定义 1:1。但是在数据库中,您不能在两个表中都有 FK,因此只有数据库中的依赖表需要它的 PK 映射到主表中现有 PK 的约束。

    最后,如果您不总是处理完整的图表,我理解您不希望 EF 强制执行这种关系的原因。我认为 1:1 关系是 EF 关系映射中最令人困惑的,我总是发现自己不得不回去提醒规则以及事情应该如何工作。

    【讨论】:

      【解决方案3】:

      老问题。但由于 EF6 仍在使用,甚至可用于 .Net 标准并且这个问题可能是一个真正的麻烦,我认为值得一提的是我在其他答案中找不到的东西。

      HasRequired - WithRequiredPrincipalHasOptional - WithRequired 确实产生相同的数据库架构和相同的运行时行为。也就是说,使用这两种映射都可以保存主体 没有依赖实体,并在以后删除依赖实体。 HasRequired 就这么多。

      但是有一种方法可以让 EF 在创建实体时验证所需的关系,即简单地添加一个[Required] 属性:

      public class RequiredPrincipalA
      {
          public int Id { get; set; }
      
          [Required] // <== here
          public virtual RequiredDependentA DependentA { get; set; }
      }
      
      public class RequiredDependentA
      {
          public int Id { get; set; }
          public virtual RequiredPrincipalA PrincipalA { get; set; }
      }
      

      如前所述,仅在创建实体时。仍然可以设置RequiredPrincipalA.RequiredDependentA = null 并成功保存。但我认为,幸运的是,在代码中发生这种情况的可能性远低于忘记设置所需的依赖项。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-10
        • 1970-01-01
        相关资源
        最近更新 更多