You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 21:40:35