EF Core 一对多映射到没有键的表

EF Core one-to-many mapping to tables with no keys

我正在尝试将 tables 从旧 IBM Db2 数据库映射到使用 EntityFramework Core 2.2 的实体,但这些 tables 根本没有任何键(是的,我也很震惊)而且我不确定我是否被允许将它们添加到这个自石器时代以来一直 运行 的古老野兽中。

到目前为止,我已经通过对关系进行逆向工程并找到要使用的潜在键来让 EF 可以愉快地跟踪这些实体,但我面临着一对多映射的问题。

我想建立从 Invoice table 到 SellerAccountDetails table 的一对多映射。我的配置:

发票实体:

public class FinvoiceConfiguration : IEntityTypeConfiguration<Finvoice>
{
    public void Configure(EntityTypeBuilder<Finvoice> builder)
    {
        // PK for Invoice
        builder.HasKey(f => new { f.FinvoiceBatchNumber, f.FinvoiceBatchRowNumber });

        // One-to-many to SellerAccountDetails table
        builder.HasMany(f => f.SellersAccountDetails)
            .WithOne(s => s.Invoice)
            .HasForeignKey(s => new
            {
                s.FinvoiceBatchNumber,
                s.FinvoiceBatchRowNumber,
                s.SellerAccountPartyIdentifier
            })
            .HasPrincipalKey(f => new
            {
                f.FinvoiceBatchNumber,
                f.FinvoiceBatchRowNumber,
                f.SellerPartyIdentifier
            });
    }
}

SellerAccountDetails 实体:

public class SellerAccountDetailsConfiguration : IEntityTypeConfiguration<SellerAccountDetails>
{
    public void Configure(EntityTypeBuilder<SellerAccountDetails> builder)
    {
        // PK for SellerAccountDetails
        builder.HasKey(s => new
        {
            s.FinvoiceBatchNumber,
            s.FinvoiceBatchRowNumber,
            s.SellerAccountPartyIdentifier,
            s.SellerAccountID // <-- Needed for uniqueness of this entity
        });

        // Many-to-one to Invoice table
        builder.HasOne(s => s.Invoice)
            .WithMany(i => i.SellersAccountDetails)
            .HasForeignKey(s => new
            {
                s.FinvoiceBatchNumber,
                s.FinvoiceBatchRowNumber,
                s.SellerAccountPartyIdentifier
            })
            .HasPrincipalKey(f => new
            {
                f.FinvoiceBatchNumber,
                f.FinvoiceBatchRowNumber,
                f.SellerPartyIdentifier
            });
    }
}

现在,当我进行查询然后调用 ToList() 时,这会起作用,在我的测试代码中,我应该为一个 Finvoice 获得 3 个 SellerAccountDetails 个实体,但是如果我不要具体化 IQueryable,我得到 6 SellerAccountDetails 一个 Finvoice,如果我再次枚举,它们会增加到 9,依此类推...

EF 不应该从 SellerAccountDetails 上配置的主键知道是什么让这个实体独一无二?

我添加了 HasPrincipalKey 因为 属性 SellerPartyIdentifier(来自 Finvoice)对应于帐户 table 上的 属性 SellerAccountPartyIdentifier ,但它不是主键。如果我删除它并只保留 2 个外键,结果是一样的。

如何进行这项工作?我可能在这里遗漏了一些明显的东西,但我没有看到。

这是我的示例查询:

var invoices = _dbContext.Invoices.Include(i => i.SellersAccountDetails).ThenInclude(s => s.Invoice).
                Where(i => i.FinvoiceBatchNumber == 13491).OrderBy(i => i.FinvoiceBatchRowNumber).Take(1);//.ToList();

谢谢!

如果 Db2 中没有 key/relationship,则无法使用 "Include" linq。

你必须使用下面的

var invoices = (from a in _dbContext.Invoices.Where(x => x.FinvoiceBatchNumber == 13491) 
                from b in _dbContext.SellersAccountDetails.Where(x => x.InvoiceId = a.Id).DefaultOrEmpty()
                select a, b).Take(1);

已解决!问题是实体属性的 getter 是 trimming 字段:

private string _sellerAccountID;

[Column("FV77A", TypeName = "char(35)")]
[Required, DefaultValue("")]
[MinLength(0), MaxLength(35)]
public string SellerAccountID
{
    get { return _sellerAccountID.Trim(); } // <-- Trim() is the culprit
    set { _sellerAccountID = value; }
}

出于某种原因,这混淆了 EF 跟踪器,因为如果我也使用 AsNoTracking 禁用跟踪(或通过执行 optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); 全局设置它,那么它也可以正常工作。

我删除了 trim(和私有字段),并在 DTO 级别上使用 AutoMapper 进行了 trimming,如下所示: CreateMap<string, string>().ConvertUsing((s, d) => s.Trim());

现在无论我枚举 IQueryable 多少次,我总是得到 3 个结果。