如何基于多列在.NET中创建Invoice与ContractAccount的多对多关系
解决方案
.NET 生态下的EF(包括EF Core和EF6)完全支持基于多列组合的关联关系配置,你不需要手动写关联筛选逻辑,具体实现步骤如下:
实现步骤
步骤1:给实体添加导航属性
首先在Invoice实体类中添加关联ContractAccount的导航属性:
public class Invoice { // 原有属性 Id、InvoiceNumber、CustomerId、ConsumptionPointId 等 public ICollection<ContractAccount> ContractAccounts { get; set; } }
然后在ContractAccount实体类中添加反向导航属性:
public class ContractAccount { // 原有属性 Id、ContractAccountNumber、CustomerId、ConsumptionPointId、IsCurrentDelivery 等 public ICollection<Invoice> Invoices { get; set; } }
步骤2:配置复合键多对多关系
在你继承自DbContext的上下文类的OnModelCreating方法中添加如下配置:
若使用EF Core:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置Invoice和ContractAccount的多对多复合关联 modelBuilder.Entity<Invoice>() .HasMany(i => i.ContractAccounts) .WithMany(ca => ca.Invoices) .UsingEntity( j => j .HasOne<ContractAccount>() .WithMany() .HasForeignKey("CustomerId", "ConsumptionPointId") .HasPrincipalKey<ContractAccount>(ca => new { ca.CustomerId, ca.ConsumptionPointId }), j => j .HasOne<Invoice>() .WithMany() .HasForeignKey("CustomerId", "ConsumptionPointId") .HasPrincipalKey<Invoice>(i => new { i.CustomerId, i.ConsumptionPointId }) ); // 可选:添加全局筛选器,默认只查询IsCurrentDelivery为true的ContractAccount,省去手动加条件 modelBuilder.Entity<ContractAccount>() .HasQueryFilter(ca => ca.IsCurrentDelivery); }
若使用EF6:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Invoice>() .HasMany(i => i.ContractAccounts) .WithMany(ca => ca.Invoices) .Map(m => { m.MapLeftKey("CustomerId", "ConsumptionPointId"); m.MapRightKey("CustomerId", "ConsumptionPointId"); m.ToTable("InvoiceContractAccountMappings"); }); }
最终调用方式
配置完成后,你就可以直接通过导航属性获取对应ContractAccount,不需要手动写关联筛选逻辑:
// 直接获取对应有效的ContractAccount var targetContractAccount = invoice.ContractAccounts.FirstOrDefault();
注意事项
- 确保
Invoice和ContractAccount中的CustomerId、ConsumptionPointId属性的类型完全一致,否则关联配置会报错 - 如果你后续需要查询
IsCurrentDelivery为false的记录,调用IgnoreQueryFilters()方法即可取消全局筛选器的限制:var allAccounts = context.ContractAccounts.IgnoreQueryFilters().ToList(); - 如果需要对中间表做额外的字段扩展,可以自定义中间表实体类替换隐式中间表,配置逻辑基本一致。
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

