EF Core 子实体未与父实体一起加载
The EF Core child entity isn't loaded along with the parent
数据库
我正在使用一个 SQL 服务器数据库,它有两个 table:INVOICE_HEADER
和 INVOICE_ROW
。
INVOICE_HEADER
有两个字段:
INTERNAL_ID
,输入int
和主键,
INV_NUM
,类型 varchar(13)
,每个业务逻辑都是唯一的,但不是每个数据库结构。
INVOICE_ROW
有三个:
INTERNAL_ID
,输入int
和主键,
INV_NUM
,键入 varchar(13)
,
INV_ROW_NUM
,类型 int
,每个业务逻辑也是唯一的。
来自 INVOICE_ROW
且 INV_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_HEADER
和INVOICE_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);
}
我觉得所有这些提到 HasMany
、WithOne
都是必要的,以弥补缺少真正可用的主键。
结果
我尝试了以下方法:
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
已正确加载。
感谢所有花时间阅读问题的人!
数据库
我正在使用一个 SQL 服务器数据库,它有两个 table:INVOICE_HEADER
和 INVOICE_ROW
。
INVOICE_HEADER
有两个字段:
INTERNAL_ID
,输入int
和主键,INV_NUM
,类型varchar(13)
,每个业务逻辑都是唯一的,但不是每个数据库结构。
INVOICE_ROW
有三个:
INTERNAL_ID
,输入int
和主键,INV_NUM
,键入varchar(13)
,INV_ROW_NUM
,类型int
,每个业务逻辑也是唯一的。
来自 INVOICE_ROW
且 INV_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_HEADER
和INVOICE_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);
}
我觉得所有这些提到 HasMany
、WithOne
都是必要的,以弥补缺少真正可用的主键。
结果
我尝试了以下方法:
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
已正确加载。
感谢所有花时间阅读问题的人!