执行Add-Migration时遇Skip Navigation未配置反向导航错误
问题描述
开发C# Web API项目时,执行Add-Migration命令遇到如下错误:
Unable to create a 'DbContext' of type ''. The exception 'The skip navigation 'Class.Staffs' doesn't have an inverse navigation configured. Every skip navigation should have an inverse skip navigation.' was thrown while attempting to create an instance.
模型结构
Class类
public class Class : IBaseObject { [Key] public int ID { get; set; } public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public string Name { get; set; } = null!; public string Room { get; set; } = null!; public int NoPeople { get; set; } // Number of people in the class public virtual List<Person> People { get; set; } = null!; public virtual List<Staff> Staffs { get; set; } = null!; }
Person类
public class Person : IBaseObject { [Key] public int ID { get; set; } public string Name { get; set; } = null!; public bool Present { get; set; } public string Email { get; set; } = null!; public virtual List<Class> Classes { get; set; } = null!; }
Staff类(继承Person)
public class Staff : Person { private string Password; public List<Session> Sessions { get; set; } public bool checkPassword(string attempt) { string hashed = Crypto.HashPassword(attempt); return Crypto.VerifyHashedPassword(hashed, Password); } public void setPassword(string newPassword) { Password = Crypto.HashPassword(newPassword); } }
连接表ClassList
public class ClassList { public Person Person { get; set; } = null!; public Class Class { get; set; } = null!; public int PersonID { get; set; } public int ClassID { get; set; } }
现有DbContext配置
最初的关系配置:
public class DataContext : DbContext { public DataContext(DbContextOptions<DataContext> options) : base(options) { } public DbSet<Class> Classes => Set<Class>(); public DbSet<Person> People => Set<Person>(); public DbSet<ClassList> ClassList => Set<ClassList>(); public DbSet<Staff> Staffs => Set<Staff>(); public DbSet<StaffClass> StaffClass => Set<StaffClass>(); protected override void OnModelCreating(ModelBuilder modelBuilder) { // Configure the Person-Class many-to-many relationship modelBuilder.Entity<ClassList>() .HasKey(cl => new { cl.PersonID, cl.ClassID }); modelBuilder.Entity<Person>() .HasMany(p => p.Classes) .WithMany(p => p.People) .UsingEntity<ClassList>( r => r.HasOne(x => x.Class) .WithMany() .HasForeignKey(x => x.ClassID), l => l.HasOne(x => x.Person) .WithMany() .HasForeignKey(x => x.PersonID) ); modelBuilder.Entity<Class>() .HasMany(p => p.People) .WithMany(p => p.Classes) .UsingEntity<ClassList>( r => r.HasOne(x => x.Person) .WithMany() .HasForeignKey(x => x.PersonID), l => l.HasOne(x => x.Class) .WithMany() .HasForeignKey(x => x.ClassID) ); // Configure the Staff-Class many-to-many relationship modelBuilder.Entity<StaffClass>() .HasKey(cl => new { cl.StaffID, cl.ClassID }); modelBuilder.Entity<Staff>() .HasMany(s => s.Classes) .WithMany(c => c.Staffs) .UsingEntity<StaffClass>( r => r.HasOne(x => x.Class) .WithMany() .HasForeignKey(x => x.ClassID), l => l.HasOne(x => x.Staff) .WithMany() .HasForeignKey(x => x.StaffID) ); modelBuilder.Entity<Class>() .HasMany(s => s.Staffs) .WithMany(c => c.Classes) .UsingEntity<StaffClass>( r => r.HasOne(x => x.Staff) .WithMany() .HasForeignKey(x => x.StaffID), l => l.HasOne(x => x.Class) .WithMany() .HasForeignKey(x => x.ClassID) ); // Other unrelated code here } } }
尝试简化配置后仍报错:
modelBuilder.Entity<Person>() .HasMany(p => p.Classes) .WithMany(p => p.People) .UsingEntity<ClassList>(); modelBuilder.Entity<Staff>() .HasMany(s => s.Classes) .WithMany(c => c.Staffs) .UsingEntity<StaffClass>();
问题原因
- Staff类缺少独立导航属性:Staff继承自Person,Person的
Classes导航属性已绑定到Person-Class的多对多关系,无法同时作为Staff-Class关系的反向导航,EF Core无法区分两组独立关系。 - 重复配置关系:最初的双向重复配置导致EF Core映射逻辑混乱。
- StaffClass实体定义不完整:未提供的
StaffClass若未包含对应导航属性和外键,会导致EF Core无法识别连接关系。
解决步骤
1. 给Staff类添加专属导航属性
在Staff类中新增独立的Class集合导航,与Person的Classes区分:
public class Staff : Person { private string Password; public List<Session> Sessions { get; set; } // 新增Staff专属的Class导航属性 public new virtual List<Class> Classes { get; set; } = null!; public bool checkPassword(string attempt) { string hashed = Crypto.HashPassword(attempt); return Crypto.VerifyHashedPassword(hashed, Password); } public void setPassword(string newPassword) { Password = Crypto.HashPassword(newPassword); } }
2. 完善StaffClass连接实体
确保StaffClass包含正确的导航属性和外键:
public class StaffClass { public Staff Staff { get; set; } = null!; public Class Class { get; set; } = null!; public int StaffID { get; set; } public int ClassID { get; set; } }
3. 修正DbContext关系配置
删除重复配置,仅保留单向清晰的关系映射:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置Person-Class多对多 modelBuilder.Entity<ClassList>() .HasKey(cl => new { cl.PersonID, cl.ClassID }); modelBuilder.Entity<Person>() .HasMany(p => p.Classes) .WithMany(c => c.People) .UsingEntity<ClassList>( j => j.HasOne(cl => cl.Class).WithMany().HasForeignKey(cl => cl.ClassID), j => j.HasOne(cl => cl.Person).WithMany().HasForeignKey(cl => cl.PersonID) ); // 配置Staff-Class多对多 modelBuilder.Entity<StaffClass>() .HasKey(sc => new { sc.StaffID, sc.ClassID }); modelBuilder.Entity<Staff>() .HasMany(s => s.Classes) .WithMany(c => c.Staffs) .UsingEntity<StaffClass>( j => j.HasOne(sc => sc.Class).WithMany().HasForeignKey(sc => sc.ClassID), j => j.HasOne(sc => sc.Staff).WithMany().HasForeignKey(sc => sc.StaffID) ); // 其他无关配置 }
4. 重新执行迁移
清理之前的错误迁移文件,然后执行命令:
Add-Migration InitialCreate -Force Update-Database
内容的提问来源于stack exchange,提问作者quantumintern
相关产品推荐
相关产品推荐

