EF7多对多(交叉表)Skip Navigation配置报错:未关联外键
EF7多对多关系配置报错:跳过导航属性未关联外键
正在将项目从NHibernate迁移至EF7,由于EF7与旧版EF配置逻辑不同,参照官方示例配置基于交叉表的多对多关系时,运行时提示对象属性未关联外键。
实体类定义
public partial class Casino { // other properties private IList<Country> _prohibitedCountries; public IList<Country> ProhibitedCountries { get { return _prohibitedCountries; } protected set { _prohibitedCountries = value; } } } public partial class Country { // other properties private IList<Casino> _prohibitedCasinos; public IList<Casino> ProhibitedCasinos { get { return _prohibitedCasinos; } protected set { _prohibitedCasinos = value; } } }
业务逻辑:一个Casino对应多个受限国家,一个Country对应多个受限赌场。
DbContext配置
public class DsDbContext : DbContext { public DbSet<Casino> Casinos { get; set; } public DbSet<Country> Countries { get; set; } // Other collections... protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { IConfigurationRoot configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .Build(); var connectionString = configuration.GetConnectionString("DataStoreDatabase"); optionsBuilder.UseSqlServer(connectionString); } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder .Entity<Casino>() .HasMany(a => a.ProhibitedCountries) .WithMany(b => b.ProhibitedCasinos) .UsingEntity<Dictionary<string, object>>( "Casino_Prohibited_Country", j => j .HasOne<Country>() .WithMany() .HasForeignKey("CountryId") .HasConstraintName("FK_CasinoProhibitedCountry_Country") .OnDelete(DeleteBehavior.Cascade), j => j .HasOne<Casino>() .WithMany() .HasForeignKey("CasinoId") .HasConstraintName("FK_CasinoProhibitedCountry_Casino") .OnDelete(DeleteBehavior.Cascade) ); modelBuilder .Entity<Country>() .HasMany(c => c.ProhibitedCasinos) .WithMany(c => c.ProhibitedCountries) .UsingEntity<Dictionary<string, object>>( "Casino_Prohibited_Country", j => j .HasOne<Casino>() .WithMany() .HasForeignKey("CasinoId") .HasConstraintName("FK_CasinoProhibitedCountry_Casino") .OnDelete(DeleteBehavior.Cascade), j => j .HasOne<Country>() .WithMany() .HasForeignKey("CountryId") .HasConstraintName("FK_CasinoProhibitedCountry_Country") .OnDelete(DeleteBehavior.Cascade) ); } }
数据库交叉表脚本
CREATE TABLE [dbo].[Casino_Prohibited_Country]( [CasinoId] [int] NOT NULL, [CountryId] [int] NOT NULL ) ON [PRIMARY] GO ALTER TABLE [dbo].[Casino_Prohibited_Country] WITH NOCHECK ADD CONSTRAINT [FK_CasinoProhibitedCountry_Casino] FOREIGN KEY([CasinoId]) REFERENCES [dbo].[Casino] ([Id]) GO ALTER TABLE [dbo].[Casino_Prohibited_Country] CHECK CONSTRAINT [FK_CasinoProhibitedCountry_Casino] GO ALTER TABLE [dbo].[Casino_Prohibited_Country] WITH NOCHECK ADD CONSTRAINT [FK_CasinoProhibitedCountry_Country] FOREIGN KEY([CountryId]) REFERENCES [dbo].[Country] ([Id]) GO ALTER TABLE [dbo].[Casino_Prohibited_Country] CHECK CONSTRAINT [FK_CasinoProhibitedCountry_Country] GO
报错信息
System.InvalidOperationException: 'The skip navigation 'Country.ProhibitedCasinos' doesn't have a foreign key associated with it. Every skip navigation must have a configured foreign key.'
执行基础CRUD测试时,在基础仓储构造函数的context.Set<TEntity>()行抛出上述错误。EF可正常读取无关联实体,但无法识别已显式配置的外键。
解决方案
1. 移除重复的多对多配置
EF7中,HasMany/WithMany已经完成了双向导航属性的关联,重复在两个实体上配置同一组多对多关系会导致EF元数据混淆,无法正确识别外键映射。只需要保留其中一侧的配置即可:
修正后的OnModelCreating代码:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 仅配置一次多对多关系,双向导航会自动关联 modelBuilder .Entity<Casino>() .HasMany(a => a.ProhibitedCountries) .WithMany(b => b.ProhibitedCasinos) .UsingEntity<Dictionary<string, object>>( "Casino_Prohibited_Country", j => j .HasOne<Country>() .WithMany() .HasForeignKey("CountryId") .HasConstraintName("FK_CasinoProhibitedCountry_Country") .OnDelete(DeleteBehavior.Cascade), j => j .HasOne<Casino>() .WithMany() .HasForeignKey("CasinoId") .HasConstraintName("FK_CasinoProhibitedCountry_Casino") .OnDelete(DeleteBehavior.Cascade) ); // 移除Country实体上的重复配置 }
2. 确保导航属性集合已初始化
EF需要集合实例才能正确操作关联关系,建议在实体构造函数中初始化私有字段:
public partial class Casino { public Casino() { _prohibitedCountries = new List<Country>(); } // other properties private IList<Country> _prohibitedCountries; public IList<Country> ProhibitedCountries { get { return _prohibitedCountries; } protected set { _prohibitedCountries = value; } } } public partial class Country { public Country() { _prohibitedCasinos = new List<Casino>(); } // other properties private IList<Casino> _prohibitedCasinos; public IList<Casino> ProhibitedCasinos { get { return _prohibitedCasinos; } protected set { _prohibitedCasinos = value; } } }
内容的提问来源于stack exchange,提问作者Adam Hey
相关产品推荐
相关产品推荐

