Cosa c'è di sbagliato nel mio codice che ottengo sotto l'errore:
Impossibile determinare un ordine valido per operazioni dipendenti. Potrebbero esistere dipendenze a causa di vincoli di chiave esterna, requisiti del modello o valori generati dal negozio
Codice:
Cibo di classe:
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; }
}
Persona di classe:
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; }
}
Metodo del seme:
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 }
});
}
Ciò accade perché Entity Framework, per convenzione, presuppone che la proprietà inversa di Person.BestFoodId
sia Food.Persons
. Dichiarate diversamente: Person.BestFood
e Food.Persons
sono considerati i due estremi di un'associazione uno-a-molti, con Person.BestFoodId
come chiave esterna.
Puoi verificarlo aggiungendo un attributo [InverseProperty]
a BestFood
:
public class Person
{
...
[ForeignKey("BestFoodId")]
[InverseProperty("Persons")]
public Food BestFood { get; set; }
...
}
Questo provoca lo stesso errore.
Questo errore - nessun ordine valido - indica sempre un problema con pollo e uova. Nel tuo caso, EF cerca di inserire gli alimenti, che hanno bisogno dell'ID generato della persona inserita come chiave esterna, mentre la persona inserita ha bisogno dell'ID generato dell'alimento foo2
inserito.
In realtà, Person
and Food
hanno due associazioni:
Food
può essere il BestFood
di n persone. Food
s può essere il FavoriteFoods
di m persone. Nel tuo modello, BestFood
non ha una proprietà inversa, che potrebbe essere stata qualcosa come ...
public virtual ICollection<Person> BestFoodOf { get; set; }
... ma non è necessario e poiché manca, oscura il modo in cui EF infetta le associazioni.
È possibile risolvere questo mappando in modo esplicito le associazioni, per esempio nel OnModelCreating
esclusione del DbContext
sottoclasse:
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"));
Ho commentato WillCascadeOnDelete(false)
. Devi aggiungere questa riga o aggiungere ...
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
... per impedire più percorsi di eliminazione in cascata (restrizioni di SQL Server).
Avendo questo in atto, EF sa come determinare un ordine valido per gli inserti: inserirà prima gli alimenti, quindi inserirà la persona (usando l'ID foo2
generato come chiave esterna) e quindi i record di giunzione nella tabella PersonFavoriteFood
.
Sembra che tu abbia una dipendenza circolare.
Le risposte sono qui:
Miglioramenti opzionali:
Dovresti dichiarare la tua proprietà di navigazione come virtuale !
Se si utilizza C # 6.0 o versioni successive, modificare la definizione di annotazione dei dati [ForeignKeyAttribute]
su [ForeignKey([nameof(BestFoodId))]
per evitare errori con nomi di proprietà codificati. nameof
è una funzione di compilazione davvero interessante! :)