EF Core 子实体未与父实体一起加载

The EF Core child entity isn't loaded along with the parent

数据库

我正在使用一个 SQL 服务器数据库,它有两个 table:INVOICE_HEADERINVOICE_ROWINVOICE_HEADER 有两个字段:

INVOICE_ROW有三个:

来自 INVOICE_ROWINV_NUM 的行与来自 INVOICE_HEADER 的行具有相同值的行在业务逻辑术语中构成相同的发票,因此 INVOICE_HEADER.INV_NUM 和(INVOICE_ROW.INV_Num, INVOICE_ROW.INV_ROW_NUM) 就我们的查询而言充当事实上的主键。

INVOICE_HEADER.INTERNAL_ID 中找到的值与在 INVOICE_ROW.INTERNAL_ID 中找到的值不同,而且 table 中的任何字段似乎都没有对应于 INTERNAL_ID 的值在另一个。

所以没有实际的外键将 table 链接到另一个。

不用说这个数据库是第三方的,结构是固定的,还有很多table,而且INVOICE_HEADERINVOICE_ROW都有很多更多领域?好吧,无论如何我刚刚做了。

实际上,INTERNAL_ID 几乎没有出现在描述数据库结构的文档中,一个字段一个字段,并且只出现在其他几个 table 中。

使用Entity Framework核心访问数据库

由于上述原因,我无法使用数据库中定义的主键。

所以我创建了两个 类:

    public class InvoiceHeader
    {
        public int InternalId { get; set; }
        public string InvoiceNum { get; set; }

        public virtual List<InvoiceRow> InvoiceRows { get; set; }
    }

    public class InvoiceRow
    {
        public int InternalId { get; set; }
        public string InvoiceNum { get; set; }
        public int? RowNum { get; set; }

        public virtual InvoiceHeader InvHeader { get; set; }
    }

还有上下文:

        public DbSet<InvoiceHeader> RepoInvHeader { get; set; }
        public DbSet<InvoiceRow> RepoInvRow { get; set; }

        protected override void OnModelCreating(ModelBuilder p_mbuModel)
        {
            base.OnModelCreating(p_mbuModel);

            // Table name
            p_mbuModel.Entity<InvoiceHeader>().ToTable("INVOICE_HEADER");

            // Primary key
            p_mbuModel.Entity<InvoiceHeader>().HasKey(t => new { t.InternalId }).
                                               HasName("PK_INTERNAL_ID_INVOICE_HEADER");

            // Fields
            p_mbuModel.Entity<InvoiceHeader>().Property(t => t.InternalId).IsRequired();
            p_mbuModel.Entity<InvoiceHeader>().Property(t => t.InternalId).HasColumnType("int");
            p_mbuModel.Entity<InvoiceHeader>().Property(t => t.InvoiceNum).HasColumnName("INV_NUM");
            p_mbuModel.Entity<InvoiceHeader>().Property(t => t.InvoiceNum).HasColumnType("varchar(13)");
            p_mbuModel.Entity<InvoiceHeader>().Property(t => t.InvoiceNum).HasMaxLength(13);

            // Foreign keys
            p_mbuModel.Entity<InvoiceHeader>().HasMany(h => h.InvoiceRows).
                                               WithOne(r => r.InvHeader).
                                               HasPrincipalKey(h => h.InvoiceNum).
                                               HasForeignKey(r => r.InvoiceNum);

            // Table name
            p_mbuModel.Entity<InvoiceRow>().ToTable("INVOICE_ROW");

            // Primary key
            p_mbuModel.Entity<InvoiceRow>().HasKey(t => new { t.InternalId }).
                                            HasName("PK_INTERNAL_ID_INVOICE_ROW");

            // Fields
            p_mbuModel.Entity<InvoiceRow>().Property(t => t.InternalId).IsRequired();
            p_mbuModel.Entity<InvoiceRow>().Property(t => t.InternalId).HasColumnType("int");
            p_mbuModel.Entity<InvoiceRow>().Property(t => t.InvoiceNum).HasColumnName("INV_NUM");
            p_mbuModel.Entity<InvoiceRow>().Property(t => t.InvoiceNum).HasColumnType("varchar(13)");
            p_mbuModel.Entity<InvoiceRow>().Property(t => t.RowNum).HasColumnName("INV_ROW_NUM");
            p_mbuModel.Entity<InvoiceRow>().Property(t => t.RowNum).HasColumnType("int");

            // Foreign keys
            p_mbuModel.Entity<InvoiceRow>().HasOne(r => r.InvHeader).
                                            WithMany(h => h.InvoiceRows).
                                            HasForeignKey(r => r.InvoiceNum).
                                            HasPrincipalKey(h => h.InvoiceNum);
        }

我觉得所有这些提到 HasManyWithOne 都是必要的,以弥补缺少真正可用的主键。

结果

我尝试了以下方法:

            InvoiceHeader z_objHeader = z_dbcContexteSage.repoInvHeader.Include(h => h.InvoiceRows).FirstOrDefault(h => h.Piece == "XXXX");

在调试时,z_objHeader 适当地具有我期望来自 INVOICE_HEADER 的字段的值,但它的 InvoiceRows 属性 是 null。 (我也试过 foreach (InvoiceRow z_objRow in z_objHeader.InvoiceRows):它抛出一个 NullReferenceException。)

现在,如果我添加这个和运行它,

            InvoiceRow z_objRow = z_dbcContexteSage.repoInvRow.FirstOrDefault(r => r.Piece == "XXXX");

查看 z_objHeader.InvoiceRows 表明它现在包含 一个 InvoiceRow.

更好,在运行宁

之后
            List<InvoiceRow> z_lisRows = z_dbcContexteSage.repoInvRow.Where(r => r.Piece == "XXXX").ToList();

z_objHeader.InvoiceRows 现在有 所有 我希望它有的行。

所以我可以使用它,但我仍然觉得那些显式从 INVOICE_ROW 加载的命令不需要,甚至没有用。

我在 Web 上进行了广泛的搜索,但似乎无法找出我忽略或遗漏的内容。

我想问题可能在于 official-but-unusable-primary-key/de-facto-primary-key 东西。

有什么想法吗?

这不是我忘记输入的内容,而是我(或其他人)在某个时候输入的内容。

有一个

using System.Data.Entity;

在测试 class 中,这显然是在扰乱 Update() 方法。现在它不见了,属性 InvoiceRows 已正确加载。

感谢所有花时间阅读问题的人!