【发布时间】:2021-02-16 06:56:36
【问题描述】:
数据库
我正在使用带有两个表的 SQL Server 数据库: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 的行与来自INVOICE_HEADER 的行具有相同值的INV_NUM 在业务逻辑术语中构成相同的发票,因此INVOICE_HEADER.INV_NUM 和(INVOICE_ROW.INV_Num, INVOICE_ROW.INV_ROW_NUM) 用作de就我们的查询而言,事实主键。
在INVOICE_HEADER.INTERNAL_ID 中找到的值与在INVOICE_ROW.INTERNAL_ID 中找到的值不同,并且任何一个表中的字段似乎都没有与另一个表中的INTERNAL_ID 对应的值。
因此没有实际的外键将任何一个表链接到另一个表。
我需要提到数据库来自第三方,它的结构是固定的,它有更多的表,INVOICE_HEADER 和INVOICE_ROW 都有更多的字段?无论如何,我刚刚做到了。
实际上,INTERNAL_ID 甚至几乎没有出现在描述数据库结构的文档中,逐个字段,并且仅用于其他几个表。
使用 Entity Framework Core 访问数据库
由于上述原因,我无法使用数据库中定义的主键。
所以我创建了两个类:
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 显式加载的命令是不需要的,甚至是有用的。
我在网上四处张望,似乎找不到我忽略或遗漏的内容。
我想问题可能在于官方但无法使用的主键/事实上的主键。
有什么想法吗?
【问题讨论】:
-
我认为您应该研究备用键。 docs.microsoft.com/en-us/ef/core/modeling/…
-
@jeroenh:我做到了,谢谢!在您的提示下,我再次查看,发现拆分查询可能会保留答案,但文档说它们将在 5.0 版中可用(有趣的是,以现在时态表示,而我昨天更新了 EF Core,它仍然是 3.1.9) .
-
@jeroenh:我尝试使用
AsSplitQuery(),然后才知道它无法编译,因为它还不可用。它让我注意到并评论了using System.Data.Entity;。现在它已经完成了——猜猜看——InvoiceRows被正确加载了!谢谢。
标签: c# sql-server entity-framework-core querying