【问题标题】:Entity Framework: Foreign Key in code first实体框架:代码中的外键优先
【发布时间】:2016-08-05 15:46:52
【问题描述】:

我得到以下错误的代码有什么问题:

无法确定相关操作的有效顺序。由于外键约束、模型要求或存储生成的值,可能存在依赖关系

代码:

类食物:

public class Food
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public short Id { get; set; }

    //some Property
    public string Name { get; set; }

    public virtual ICollection<Person> Persons { get; set; }
}

类人:

public class Person
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    //some Property
    public string FirstName { get; set; }

    [ForeignKey("BestFoodId")]
    public Food BestFood { get; set; }
    public short BestFoodId { get; set; }

    public virtual ICollection<Food> FavoriteFoods { get; set; }
}

种子方法:

 protected override void Seed(MyContext context)
    {
        Food food1 = new Food() { Name = "foo1" };
        Food food2 = new Food() { Name = "foo2" };
        Food food3 = new Food() { Name = "foo3" };

        context.Persons.AddOrUpdate(new Person()
        {
            FirstName = "Jack",
            BestFood = food2,
            FavoriteFoods = new List<Food>() { food1, food2, food3 }
        });

    }

【问题讨论】:

    标签: c# entity-framework foreign-keys entity-framework-6 code-first


    【解决方案1】:

    错误原因:关联混乱

    这是因为实体框架按照惯例假定Person.BestFoodId逆属性Food.Persons。换一种说法:Person.BestFoodFood.Persons 被假定为一对多关联的两端,以 Person.BestFoodId 作为外键。

    您可以通过将[InverseProperty] 属性添加到BestFood 来验证这一点:

    public class Person
    {
        ...
        [ForeignKey("BestFoodId")]
        [InverseProperty("Persons")]
        public Food BestFood { get; set; }
        ...
    }
    

    这会导致同样的错误。

    这个错误——没有有效的排序——总是表示先有鸡还是先有蛋的问题。在您的情况下,EF 尝试插入食物,这需要插入的人生成的 Id 作为外键,而插入的人需要插入的 foo2 food 的生成的 Id。

    解决方案:显式映射关联

    实际上,PersonFood 有两个关联:

    • 1-n:Food 可以是 n 个人中的BestFood
    • n-m: n Foods 可以是 m 个人中的 FavoriteFoods

    在您的模型中,BestFood 没有逆属性,可能类似于 ...

    public virtual ICollection<Person> BestFoodOf { get; set; }
    

    ...但这不是必需的,因为它丢失了,它掩盖了 EF 如何推断关联。

    您可以通过显式映射关联来解决此问题,例如在您的 DbContext 子类的 OnModelCreating 覆盖中:

    modelBuilder.Entity<Person>()
                .HasRequired(p => p.BestFood)
                .WithMany() // No inverse property
                .HasForeignKey(p => p.BestFoodId)
                //.WillCascadeOnDelete(false)
                ;
    
    modelBuilder.Entity<Person>()
                .HasMany(p => p.FavoriteFoods)
                .WithMany(f => f.Persons)
                .Map(m => m.MapLeftKey("PersonId")
                           .MapRightKey("FoodId")
                           .ToTable("PersonFavoriteFood"));
    

    我已经注释掉了WillCascadeOnDelete(false)。您要么必须添加此行,要么添加 ...

    modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
    

    ... 防止多个级联删除路径(一个 SQL Server 限制)。

    有了这个,EF 知道如何确定插入的有效顺序:它将首先插入食物,然后插入人(使用生成的 foo2 Id 作为外键),然后在PersonFavoriteFood 表。

    【讨论】:

      【解决方案2】:

      看起来你有一个循环依赖。

      答案在这里:


      可选改进:

      • 您应该将您的navigation property 声明为virtual

      • 如果您使用的是 C# 6.0 或更高版本,请将您的 [ForeignKeyAttribute] 数据注释定义更改为 [ForeignKey([nameof(BestFoodId))] 以避免硬编码属性名称出现错误。 nameof 是一个非常酷的编译器功能! :)

      【讨论】:

      • 感谢您的关注 ;)
      • 不客气,我的朋友!但我希望我至少能帮到你一点点;)
      • 你很棒。在提问之前我搜索并阅读了几个教程,但每个场景都对自己有好处,我无法解决我的问题。我的问题的代码很简单。 Evrey 身体测试它和你的答案很容易。
      猜你喜欢
      • 2011-08-05
      • 1970-01-01
      • 2012-05-10
      • 2015-02-24
      • 2021-01-05
      • 2011-08-05
      • 1970-01-01
      相关资源
      最近更新 更多