【问题标题】:Create many-to-many relationship between two entities via multiple columns通过多个列在两个实体之间创建多对多关系
【发布时间】:2021-12-21 04:28:50
【问题描述】:

我有以下四张表:

客户

Id
FirstName
...

消费点

Id
Address
...

发票

Id
InvoiceNumber
CustomerId
ConsumptionPointId
...

合约账户

Id
ContractAccountNumber
CustomerId
ConsumptionPointId
IsCurrentDelivery
...

我想获取发票的 ContractAccountNumber。

是否可以在这两者之间创建某种关系以直接访问 Invoice 的 ContractAccount(s)?

目前我正在做类似的事情:

invoice.Customer.ContractAccounts
    .Where(ca => ca.ConsumptionPoint == invoice.ConsumptionPoint &&
    ca.IsCurrentDelivery == true).FirstOrDefault();

update 在 SQL 中我只需使用多个条件进行连接:

SELECT i.Id AS InvoiceId, ca.Id AS ContractAccountId, 
ca.ContractAccountNumber
FROM Invoices i
LEFT JOIN ContractAccounts ca
ON i.ConsumptionPointId = ca.ConsumptionPointId
AND i.CustomerId = ca.CustomerId
WHERE ca.IsCurrentDelivery = 1

更新 2:

基本上我只是想摆脱 Where-Clause 中的 ca.ConsumptionPoint == invoice.ConsumptionPoint 并想在关系中定义它。

实际上这是一个多对多的关系:一个 Invoice 可以链接到多个 ContractAccounts(通过不同的 Customer/ConsumptionPoint 组合),一个 ContractAccount 可以链接到多个 Invoices。有没有办法告诉 .net 基于两个自定义列的组合建立多对多关系?

【问题讨论】:

  • 您可以在数据库中创建一个 VIEW 以仅公开具有所需条件的数据,并在您的实际实体和您的 VIEW 之间创建 1-1 关系
  • @Cleptus 带有 VIEW 的想法听起来很有趣。我认为这应该有效。我会试试这个。是的,应该是 ca.IsCurrentDelivery 感谢您的更正。

标签: c# .net-core entity-framework-core fluent


【解决方案1】:

我认为您不需要两个密钥。使用 EF 核心,您可以创建这样的关系:

发票:

public class Invoice
{
  public int Id {get; set;}
  public int InvoiceNumber {get; set;}
  public int CustomerId {get; set;}
  public Customer Customer {get; set;}
  public int ConsumptionPointId {get; set;}

  public int ContractAccountId {get; set;}
  public ContractAccount ContractAccount {get; set;}
}
public class ContractAccount
{
  public int Id {get; set;}
  public int ContractAccountNumber {get; set;}
  public bool IsCurrentDelivery {get; set;}
}

然后配置:

modelBuilder.Entity<Invoice>()
  .HasOne(b => b.ContractAccount)
  .WithOne()
  .HasForeignKey<Invoice>(b => b.ContractAccountId);

//probably already exists in your code
modelBuilder.Entity<Invoice>()
  .HasOne(b => b.Customer )
  .WithMany()
  .HasForeignKey(b => b.CustomerId);

然后您可以直接从发票中访问它:invoice.ContractAccount

【讨论】:

  • 我相信你可能已经跳过了关系不是1-1本身而是1-1(filtered data)。作为“CustomerId/ConsumptionPointId (with IsCurrentDelivery==true) 的组合是唯一的”问题中的相关部分
  • 可能是,描述写得很复杂。另外我猜这个唯一索引有问题 - 它限制每个 CustomerId/ConsumptionPointId 有两条记录,一条 IsCurrentDelivery = true 和 other = false
  • 这个解决方案会在Invoice 实体中产生一个ContractAccountId 外键。但这不是想要的。可以将一个 ContractAccount 设置为 IsCurrentDelivery=false,并且可以为相同的 Customer/ConsumptionPoint 组合创建另一个 ContractAccount。在这种情况下,不应通过更改外键来手动更新发票。它应该“即时”更新(通过为 Customer/ConsumptionPoint 等价物请求具有当前交付的 ContractAccount)。在有问题的纯 SQL 中查看我更新的解决方案。
【解决方案2】:

经过更深入的调查,我找到了解决方案。

基本上它只是一个多对多的关系。所以我们可以为 Invoice 和 ContractAccount 模型添加适当的属性:

public class Invoice
{
    [Key]
    public long Id { get; set; }
    public Customer Customer { get; set; }
    public ConsumptionPoint ConsumptionPoint { get; set; }
    public virtual ICollection<ContractAccount> ContractAccounts { get; set; }
}

public class ContractAccount
{
    [Key]
    public long Id { get; set; }
    public Customer Customer { get; set; }
    public ConsumptionPoint ConsumptionPoint { get; set; }
    public ICollection<Invoice> Invoices { get; set; }
}

现在,我们只需要手动配置关系:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<Invoice>()
        .HasMany(i => i.ContractAccounts)
        .WithOne()
        .HasForeignKey(ca => new { ca.CustomerId, ca.ConsumptionPointId })
        .HasPrincipalKey(i => new { i.CustomerId, i.ConsumptionPointId });
    modelBuilder.Entity<ContractAccount>()
        .HasMany(ca => ca.Invoices)
        .WithOne()
        .HasForeignKey(i => new { i.CustomerId, i.ConsumptionPointId })
        .HasPrincipalKey(ca => new { ca.CustomerId, ca.ConsumptionPointId });
}

就是这样。现在我可以这样做:

invoice.ContractAccounts
    .Where(ca => ca.IsCurrentDelivery == true).FirstOrDefault();

这比以前好多了。

感谢您的 cmets,为我指明了正确的方向。

【讨论】:

    猜你喜欢
    • 2019-10-23
    • 1970-01-01
    • 2017-07-12
    • 2019-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多