【问题标题】:Entity Framework Code First - strange behaviour with circular relationship实体框架代码优先 - 具有循环关系的奇怪行为
【发布时间】:2016-02-08 12:16:25
【问题描述】:

我很难理解 Code First 如何根据模型建立关系。这是模型:

public class Person
{
    [Key]
    public string PersonName { get; set; }

    [Required]
    public virtual Nation NationOfBirth { get; set; }

    [Required]
    public virtual Nation CurrentNationOfResidence { get; set; }
}

public class Nation
{
    [Key]
    public string NationName { get; set; }

    public virtual Person CurrentSecondInCommand { get; set; }
}

起初我在创建数据库时遇到了一个错误,我可以通过添加几个模型构建器命令来解决这个问题:

modelBuilder.Entity<Person>()
    .HasRequired(p => p.NationOfBirth)
    .WithRequiredDependent()
    .WillCascadeOnDelete(false);
modelBuilder.Entity<Person>()
    .HasRequired(p => p.CurrentNationOfResidence)
    .WithRequiredDependent()
    .WillCascadeOnDelete(false);

我还补充说:

modelBuilder.Entity<Nation>()
    .HasOptional(n => n.CurrentSecondInCommand)
    .WithOptionalDependent()
    .WillCascadeOnDelete(false);

现在,当我尝试使用以下代码填充我的数据库时:

var america = new Nation { NationName = "America" };
context.Nations.Add(america);
var bush = new Person { PersonName = "Bush", CurrentNationOfResidence = america, NationOfBirth = america };
var obama = new Person { PersonName = "Obama", CurrentNationOfResidence = america, NationOfBirth = america };
var biden = new Person { PersonName = "Biden", CurrentNationOfResidence = america, NationOfBirth = america };
context.People.Add(bush);
context.People.Add(obama);
context.People.Add(biden);
context.SaveChanges();

在添加 'bush' 时,CurrentNationOfResidence 和 NationOfBirth 属性对他来说都不为空,并且设置为 'america' 对象。但是,一旦添加了“obama”,“bush”上的 CurrentNationOfResidence 和 NationOfBirth 属性都会变为空。一旦添加了“拜登”,“奥巴马”也会发生同样的情况。

如果我检查由此生成的数据库,我看不到在 People 表上创建的任何外键列,但我希望 Code First 自动为 Person 上的两个 Nation 字段生成 2 个外键(这就是我认为我的前两个模型构建器命令会做的事情——来自“WithRequiredDepedent”的描述)。更奇怪的是,数据库中添加了一个名为“America”的 Person,以及一个也称为 America 的 Nation。

如果有人能提供一些关于我的模型的一般建议,我将不胜感激。我意识到我在这里有一个循环关系,但我认为这没关系,因为国家不需要拥有 CurrentSecondInCommand。我意识到这有点复杂(或者说令人费解是一个更好的词),但我认为这是有道理的:

  • 一个人必须有一个(国家)NationOfBirth。
  • 一个人必须有一个(国家)CurrentNationOfResidence。
  • 一个国家可以选择拥有一个(个人)CurrentSecondInCommand。

非常欢迎任何建议,谢谢。

【问题讨论】:

  • 为了测试目的,你可以创建像 america1、america2 等 Nation 对象并分配给每个人并测试它
  • 另外你必须在 person 类中用 ForeignKey 属性标记 NationOfBirth...
  • @Viru 刚刚尝试了您的第一个建议。发生的情况是添加了三个“人”,分别称为“America1”、“America2”和“America3”(以及三个同名国家)。
  • @Viru WithRequiredDependent() 的注释摘要指出 - “正在配置的实体类型将是依赖的并包含主体的外键。关系目标的实体类型将是主体在关系中。” - 所以我的理解是外键将由我的模型构建器命令生成。当然,在代码中建模的优点之一是您不需要让不必要的外键属性使代码库变得混乱。如果 CF 可以(并且确实)为您做这件事,那么到处添加外键似乎并不优雅。
  • WithRequiredDependant 是对的。您的问题不是这个,而是您正在配置一对一的关系,而实际上您是多对一的

标签: c# .net entity-framework ef-code-first


【解决方案1】:

Person 与 Nation 的两种关系不是一对一,而是多对一:

  • 一个人必须有一个(国家)NationOfBirth。但是一个国家有很多人出生在那里
  • 一个人必须有一个(国家)CurrentNationOfResidence。但是一个国家有很多居民

所以你的配置应该是:

modelBuilder.Entity<Person>()
    .HasRequired(p => p.NationOfBirth)
    .WithMany()
    .WillCascadeOnDelete(false);
modelBuilder.Entity<Person>()
    .HasRequired(p => p.CurrentNationOfResidence)
    .WithMany()
    .WillCascadeOnDelete(false);

【讨论】:

  • 您好,感谢您的回答,这绝对让我走上了正确的道路。我没有意识到我的模型构建器配置指定了一对一的关系。我已经编辑了答案,以将剩下的一个关系返回给 Person 所需的配置正确,并将其标记为正确。
【解决方案2】:

Nation 类中的虚拟属性必须是ICollection&lt;Person&gt; CurrentSecondInCommand,这样您的数据库中就会有“一个国家的许多人”,关系将是“一对多”而不是“一对一”

【讨论】:

  • 但是您不能有很多 SecondInCommand 人,只有一个(或零个)。将其作为一个集合并不是对这种情况的准确模型。
猜你喜欢
  • 2012-10-26
  • 2012-10-11
  • 2017-07-05
  • 2012-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多