You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core复合主键下多对多关系配置及LINQ内联查询实现

复合主键多对多关系配置与LINQ查询实现

1. 多对多关系配置

由于Company和Customer均采用(ID, Lang)作为复合主键,中间关联表CompanyCustomers需匹配完整复合键进行外键关联,具体配置如下:

步骤1:完善实体类字段与导航属性

CompanyCustomer实体类需包含关联双方的复合主键字段及导航属性:

public class CompanyCustomer
{
    public int CompanyID { get; set; }
    public string CompanyLang { get; set; }
    public int CustomerID { get; set; }
    public string CustomerLang { get; set; }

    public Company Company { get; set; }
    public Customer Customer { get; set; }
}

Company和Customer类添加中间表导航属性:

public class Company
{
    public int ID { get; set; }
    public string Lang { get; set; }
    // 其他业务属性
    public ICollection<CompanyCustomer> CompanyCustomers { get; set; } = new List<CompanyCustomer>();
}

public class Customer
{
    public int ID { get; set; }
    public string Lang { get; set; }
    // 其他业务属性
    public ICollection<CompanyCustomer> CompanyCustomers { get; set; } = new List<CompanyCustomer>();
}

步骤2:Fluent API配置

根据业务场景选择主键配置:

  • 若同一公司与客户在不同语言下属于不同关联,设置复合主键:
modelBuilder.Entity<CompanyCustomer>(entity =>
{
    entity.HasKey(pk => new { pk.CompanyID, pk.CompanyLang, pk.CustomerID, pk.CustomerLang });

    // 关联Company实体
    entity.HasOne(cc => cc.Company)
          .WithMany(c => c.CompanyCustomers)
          .HasForeignKey(cc => new { cc.CompanyID, cc.CompanyLang })
          .OnDelete(DeleteBehavior.Cascade);

    // 关联Customer实体
    entity.HasOne(cc => cc.Customer)
          .WithMany(c => c.CompanyCustomers)
          .HasForeignKey(cc => new { cc.CustomerID, cc.CustomerLang })
          .OnDelete(DeleteBehavior.Cascade);
});
  • 若同一公司与客户跨语言为同一关联,保留原主键但需关联完整复合键:
modelBuilder.Entity<CompanyCustomer>(entity =>
{
    entity.HasKey(pk => new { pk.CompanyID, pk.CustomerID });

    entity.HasOne(cc => cc.Company)
          .WithMany(c => c.CompanyCustomers)
          .HasForeignKey(cc => new { cc.CompanyID, cc.CompanyLang })
          .OnDelete(DeleteBehavior.Cascade);

    entity.HasOne(cc => cc.Customer)
          .WithMany(c => c.CompanyCustomers)
          .HasForeignKey(cc => new { cc.CustomerID, cc.CustomerLang })
          .OnDelete(DeleteBehavior.Cascade);
});
  • EF Core 5+ 可直接配置无中间实体的多对多:
modelBuilder.Entity<Company>()
    .HasMany(c => c.Customers)
    .WithMany(c => c.Companies)
    .UsingEntity<CompanyCustomer>(
        j => j.HasOne<Customer>().WithMany().HasForeignKey(c => new { c.CustomerID, c.CustomerLang }),
        j => j.HasOne<Company>().WithMany().HasForeignKey(c => new { c.CompanyID, c.CompanyLang }),
        j => j.HasKey(pk => new { pk.CompanyID, pk.CompanyLang, pk.CustomerID, pk.CustomerLang })
    );

此方式下Company和Customer可直接添加ICollection<Customer>和ICollection<Company>导航属性,无需手动维护中间实体。

2. LINQ方法语法实现查询

方式1:使用导航属性(推荐)

利用已配置的导航属性简化查询逻辑:

public IEnumerable<Customer> GetCompanyCustomers(int companyId, string lang)
{
    return _db.Companies
        .Where(company => company.ID == companyId && company.Lang == lang)
        .SelectMany(company => company.CompanyCustomers)
        .Select(cc => cc.Customer)
        .ToList();
}

方式2:显式Join查询

若未配置导航属性,可通过多次Join实现关联查询:

public IEnumerable<Customer> GetCompanyCustomers(int companyId, string lang)
{
    return _db.Companies
        .Join(
            _db.CompanyCustomers,
            company => new { company.ID, company.Lang },
            cc => new { cc.CompanyID, cc.CompanyLang },
            (company, cc) => new { cc.CustomerID, cc.CustomerLang }
        )
        .Join(
            _db.Customers,
            cc => new { cc.CustomerID, cc.CustomerLang },
            customer => new { customer.ID, customer.Lang },
            (cc, customer) => customer
        )
        .ToList();
}

特殊场景:中间表无Lang字段

若CompanyCustomers仅存储CompanyID和CustomerID,需额外匹配语言条件:

public IEnumerable<Customer> GetCompanyCustomers(int companyId, string lang)
{
    return _db.Companies
        .Where(company => company.ID == companyId && company.Lang == lang)
        .Join(
            _db.CompanyCustomers,
            company => company.ID,
            cc => cc.CompanyID,
            (company, cc) => cc.CustomerID
        )
        .Join(
            _db.Customers.Where(c => c.Lang == lang),
            customerId => customerId,
            customer => customer.ID,
            (id, customer) => customer
        )
        .ToList();
}

内容的提问来源于stack exchange,提问作者Aftab Ahmed Kalhoro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 20:14:55