我的代码中出现以下错误:
无法确定相关操作的有效排序。由于外键约束,模型要求或存储生成的值
,可能存在依赖关系
代码:
类食品:
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 }
});
}
答案 0 :(得分:5)
这是因为按照惯例,实体框架假定Person.BestFoodId
的反向属性是Food.Persons
。换句话说:Person.BestFood
和Food.Persons
被假定为一对多关联的两端,以Person.BestFoodId
作为外键。
您可以通过向[InverseProperty]
添加BestFood
属性来验证这一点:
public class Person
{
...
[ForeignKey("BestFoodId")]
[InverseProperty("Persons")]
public Food BestFood { get; set; }
...
}
这会导致同样的错误。
此错误 - 无效订单 - 始终表示鸡与蛋的问题。在您的情况下,EF尝试插入食物,其中需要将插入的人的生成的Id作为外键,而插入的人需要插入的foo2
食物的生成的Id。
实际上,Person
和Food
有两种关联:
Food
可以是n个人的BestFood
。Food
s可以是m个人的FavoriteFoods
。在你的模型中,BestFood
没有反向属性, 可能是......
public virtual ICollection<Person> BestFoodOf { get; set; }
...但这不是必要的,因为它缺失了,它掩盖了EF如何推断关联。
您可以通过显式映射关联来解决此问题,例如在OnModelCreating
子类的DbContext
覆盖中:
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
表中的记录。
答案 1 :(得分:1)
看起来你有一个循环依赖。
答案在这里:
可选改进:
您应该将navigation property声明为virtual!
如果您使用的是C#6.0或更高版本,请将[ForeignKeyAttribute]
数据注释定义更改为[ForeignKey([nameof(BestFoodId))]
,以避免硬编码属性名称出错。 nameof
是一个非常酷的编译器功能! :)