Entity Framework:代码中的外键优先

Entity Framework: Foreign Key in code first

我的代码有什么问题导致出现以下错误:

Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values

代码:

Class食物:

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; }
}

Class 人:

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 }
        });

    }

看起来你有循环依赖。

答案 在这里:

  • Unable to determine a valid ordering for dependent operations
  • Unable to determine a valid ordering for dependent operations?
  • Entity Framework Code First Circular Dependices
  • Entity Framework 4: inheritance and Associations
  • Entity Framework Circular Reference
  • Code First Circular Reference Foreign Key Configuration
  • How to configure a self referencing object in entity framework

可选改进:

  • 您应该声明您的 navigation property as virtual!

  • 如果您使用的是 C# 6.0 或更高版本,请更改您的 [ForeignKeyAttribute] Data Annotation definition to [ForeignKey([nameof(BestFoodId))] to avoid errors with hard coded property names. nameof 是一个非常酷的编译器功能! :)

错误原因:混淆的关联

发生这种情况是因为 Entity Framework 按照惯例假设 Person.BestFoodId 逆 属性Food.Persons。换句话说:Person.BestFoodFood.Persons 被假定为一对多关联的两端,具有 Person.BestFoodId 作为外键。

您可以通过向 BestFood 添加 [InverseProperty] 属性来验证:

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

这会导致同样的错误。

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

解决方案:显式映射关联

实际上,PersonFood有两个关联:

  • 1-n: Food 可以是 BestFood 的 n 个人。
  • n-m:nFoods可以是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 服务器限制)。

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

中的连接记录