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

EF Core 6未使用指定外键却生成Shadow Key报错求助

EF Core 6.0.9 数据库优先模式下贪婪加载关联表时生成不存在的影子键问题

使用EF Core 6.0.9通过数据库优先模式生成模型,在贪婪加载关联表时无法加载BeschaeftigungsAddendum表,报错提示:Invalid column name 'BeschaeftigungBid'。该字段既未在代码中定义,数据库中也不存在,推测EF Core在此处自动生成了影子键。已按照文档配置正确外键,但问题依旧。

实体配置代码

BeschaeftigungsAddendum 配置

modelBuilder.Entity<BeschaeftigungsAddendum>(entity =>
{
    entity.HasKey(e => e.Baid);

    entity.HasIndex(e => new { e.Beschaeftigung, e.Addendum }, "IX_BeschaeftigungsAddendum")
        .IsUnique();

    entity.Property(e => e.Baid).HasColumnName("BAID");

    entity.Property(e => e.Addendum)
        .IsRequired()
        .HasMaxLength(64)
        .IsUnicode(false);

    entity.Property(e => e.Expires)
        .HasColumnType("datetime")
        .HasDefaultValueSql("('9999-12-31T23:59:59')");

    entity.Property(e => e.LastUpdate)
        .IsRequired()
        .HasMaxLength(20)
        .IsUnicode(false)
        .IsFixedLength();

    entity.Property(e => e.Lastversion)
        .IsRequired()
        .IsRowVersion()
        .IsConcurrencyToken()
        .HasColumnName("lastversion");

    entity.Property(e => e.Wert)
        .IsRequired()
        .HasMaxLength(256);

    entity.HasOne(d => d.BeschaeftigungNavigation)
        .WithMany(p => p.BeschaeftigungsAddendum)
        .HasForeignKey(d => d.Beschaeftigung)
        .HasConstraintName("FK_BeschaeftigungsAddendum_Beschaeftigung");
});

Beschaeftigung 配置

modelBuilder.Entity<Beschaeftigung>(entity =>
{
    entity.HasKey(e => e.Bid);

    entity.HasComment("Nachweis der Beschaeftigungsverhaeltnisse");

    entity.HasIndex(e => new { e.Mitarbeiter, e.Beginn, e.KostenStelle, e.DatenQuelle }, "IX_Beschaeftigung")
        .IsUnique();

    entity.Property(e => e.Bid)
        .HasColumnName("BID")
        .HasComment("Schluessel");

    entity.Property(e => e.Beginn)
        .HasColumnType("datetime")
        .HasComment("Beschaeftigungsbeginn (W3CDTF)");

    entity.Property(e => e.DienstBezeichnung).HasComment("Verweis nach Dienstbezeichnung");

    entity.Property(e => e.Einrichtung).HasComment("Verweis nach Diensstelle");

    entity.Property(e => e.Ende)
        .HasColumnType("datetime")
        .HasComment("Beschaeftigugsende (W3CDTF)");

    entity.Property(e => e.Expires)
        .HasColumnType("datetime")
        .HasDefaultValueSql("('9999-12-31T23:59:59')");

    entity.Property(e => e.KostenStelle)
        .IsRequired()
        .HasMaxLength(10)
        .IsUnicode(false)
        .HasComment("Verweis nach Diensstelle");

    entity.Property(e => e.LastUpdate)
        .IsRequired()
        .HasMaxLength(20)
        .IsUnicode(false)
        .IsFixedLength()
        .HasComment("Zeitstempel (W3CDTF)")
        .UseCollation("Latin1_General_CI_AS");

    entity.Property(e => e.Lastversion)
        .IsRequired()
        .IsRowVersion()
        .IsConcurrencyToken()
        .HasColumnName("lastversion")
        .HasComment("wegen Concurrency");

    entity.Property(e => e.Updater).HasComment("Verweis nach Person");

    entity.Property(e => e.Wd).HasColumnName("WD");

    entity.HasOne(d => d.DatenQuelleNavigation)
        .WithMany(p => p.Beschaeftigung)
        .HasForeignKey(d => d.DatenQuelle)
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("FK_Beschaeftigung_DatenQuelle");

    entity.HasOne(d => d.DienstBezeichnungNavigation)
        .WithMany(p => p.Beschaeftigung)
        .HasForeignKey(d => d.DienstBezeichnung)
        .OnDelete(DeleteBehavior.Cascade)
        .HasConstraintName("FK_Beschaeftigung_Dienstbezeichnung");

    entity.HasOne(d => d.EinrichtungNavigation)
        .WithMany(p => p.Beschaeftigung)
        .HasForeignKey(d => d.Einrichtung)
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("FK_Beschaeftigung_Einrichtung");

    entity.HasOne(d => d.MitarbeiterNavigation)
        .WithMany(p => p.Beschaeftigung)
        .HasForeignKey(d => d.Mitarbeiter)
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("FK_Beschaeftigung_Mitarbeiter");
});

模型类代码

Beschaeftigung 类

public partial class Beschaeftigung
{
    public Beschaeftigung()
    {
        BeschaeftigungsAddendum = new HashSet<BeschaeftigungsAddendum>();
        BueroAnschrift = new HashSet<BueroAnschrift>();
    }

    /// <summary>
    /// Schluessel
    /// </summary>
    public int Bid { get; set; }
    public int Mitarbeiter { get; set; }
    /// <summary>
    /// Beschaeftigungsbeginn (W3CDTF)
    /// </summary>
    public DateTime Beginn { get; set; }
    /// <summary>
    /// Beschaeftigugsende (W3CDTF)
    /// </summary>
    public DateTime Ende { get; set; }
    public bool Wd { get; set; }
    /// <summary>
    /// Verweis nach Dienstbezeichnung
    /// </summary>
    public int? DienstBezeichnung { get; set; }
    /// <summary>
    /// Verweis nach Diensstelle
    /// </summary>
    public int Einrichtung { get; set; }
    /// <summary>
    /// Verweis nach Diensstelle
    /// </summary>
    public string KostenStelle { get; set; }
    public DateTime Expires { get; set; }
    /// <summary>
    /// Verweis nach Person
    /// </summary>
    public int Updater { get; set; }
    /// <summary>
    /// Zeitstempel (W3CDTF)
    /// </summary>
    public string LastUpdate { get; set; }
    /// <summary>
    /// wegen Concurrency
    /// </summary>
    public byte[] Lastversion { get; set; }
    public int DatenQuelle { get; set; }

    public virtual DatenQuelle DatenQuelleNavigation { get; set; }
    public virtual Dienstbezeichnung DienstBezeichnungNavigation { get; set; }
    public virtual Einrichtung EinrichtungNavigation { get; set; }
    public virtual Mitarbeiter MitarbeiterNavigation { get; set; }
    public virtual ICollection<BeschaeftigungsAddendum> BeschaeftigungsAddendum { get; set; }
    public virtual ICollection<BueroAnschrift> BueroAnschrift { get; set; }
}

BeschaeftigungsAddendum 类

public partial class BeschaeftigungsAddendum
{
    public int Baid { get; set; }
    public int Beschaeftigung { get; set; }
    public string Addendum { get; set; }
    public string Wert { get; set; }
    public DateTime Expires { get; set; }
    public int Updater { get; set; }
    public string LastUpdate { get; set; }
    public byte[] Lastversion { get; set; }

    public virtual Beschaeftigung BeschaeftigungNavigation { get; set; }
}

查询代码

using (var kommDBContext = new kommdbContext(kommDBConnection))
{
    queryContext = kommDBContext.Person; 
    queryContext = queryContext
                        .Include(p => p.Mitarbeiter)
                        .ThenInclude(m => m.Beschaeftigung)
                        .ThenInclude(b => b.BeschaeftigungsAddendum);
    var sql = queryContext.ToQueryString();

    result = queryContext.ToList();
}

已尝试过重命名BeschaeftigungsAddendum.Beschaeftigung字段、显式添加外键属性、添加未映射的BeschaeftigungBid字段等方法,但EF Core仍生成不存在的影子键,生成的SQL中包含未定义的[b0].[BeschaeftigungBid]列。


排查与解决步骤

  • 显式指定关联主键:修改BeschaeftigungsAddendum的实体配置,明确指定主实体的主键,避免EF Core自动推断错误:
entity.HasOne(d => d.BeschaeftigungNavigation)
    .WithMany(p => p.BeschaeftigungsAddendum)
    .HasForeignKey(d => d.Beschaeftigung)
    .HasPrincipalKey(p => p.Bid) // 显式关联主实体主键
    .HasConstraintName("FK_BeschaeftigungsAddendum_Beschaeftigung");
  • 检查命名约定冲突:EF Core默认约定会根据导航属性名称推断外键,若导航属性名称与外键字段名称不匹配,可能触发错误推断。可以尝试将BeschaeftigungsAddendum中的Beschaeftigung字段重命名为BeschaeftigungBid,同时同步数据库字段(若允许),或保持字段名称并通过配置强制绑定。
  • 重新生成模型:使用数据库优先的脚手架命令重新生成模型,确保配置与数据库结构完全一致:
Scaffold-DbContext "你的数据库连接字符串" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force
  • 清理上下文缓存:检查DbContext中是否存在重复的关联配置,确保仅在一处定义Beschaeftigung与BeschaeftigungsAddendum的关系,避免冲突。

内容的提问来源于stack exchange,提问作者CaptainPirate

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 17:05:44