能否借助关联实体字段为Receipt与OrderLineContractNumbers创建外键?
问题描述
我有一个Receipt实体,它关联到MatchedMerchantReturnLine实体,后者包含OurOrderNumber属性。我想通过这个OurOrderNumber(加上Receipt本身无问题的Item属性),将Receipt与OrderLineContractNumbers实体建立关联。
第一次尝试及报错
尝试的EF Core配置代码:
modelBuilder.Entity<OrderLineContractNumbers>().HasMany(r => r.Receipts) .WithOne(r => r.OrderNumberDetails) .HasForeignKey(r => new { r.MatchedMerchantReturnLine.OurOrderNumber, r.Item })
运行时报错:
The expression 'r => new <>f__AnonymousType3`2(OurOrderNumber = r.MatchedMerchantReturnLine.OurOrderNumber, Item = r.Item)' is not a valid member access expression. The expression should represent a simple property or field access: 't => t.MyProperty'. When specifying multiple properties or fields, use an anonymous type: 't => new { t.MyProperty, t.MyField }'. (Parameter 'memberAccessExpression')
第二次尝试及报错
我在Receipt中添加了一个计算属性:
public string OurOrderNumber => MatchedMerchantReturnLine?.OurOrderNumber;
修改关联配置为:
modelBuilder.Entity<OrderLineContractNumbers>().HasMany(r => r.Receipts) .WithOne(r => r.OrderNumberDetails) .HasForeignKey(r => new { r.OurOrderNumber, r.Item })
此时报错:
No backing field could be found for property 'MerchantReturnReceipt.OurOrderNumber' and the property does not have a setter.
请问有没有办法实现这个关联需求?
解决方案
EF Core不支持直接通过导航属性的子属性定义外键,也无法将无存储逻辑的计算属性用作外键(外键需要映射到数据库字段,且要有可写入的逻辑)。你可以通过以下两种方式解决:
方式一:在Receipt实体添加存储型外键字段
在Receipt中新增一个可存储的OurOrderNumber字段,并确保它与MatchedMerchantReturnLine.OurOrderNumber的值保持一致:
public class Receipt { // 新增存储用的外键字段,必须包含setter public string OurOrderNumber { get; set; } public string Item { get; set; } // 原有导航属性 public MatchedMerchantReturnLine MatchedMerchantReturnLine { get; set; } public OrderLineContractNumbers OrderNumberDetails { get; set; } }
然后配置关联:
modelBuilder.Entity<OrderLineContractNumbers>() .HasMany(r => r.Receipts) .WithOne(r => r.OrderNumberDetails) .HasForeignKey(r => new { r.OurOrderNumber, r.Item });
注意:需要通过业务逻辑、EF Core变更追踪事件或数据库触发器,保证Receipt.OurOrderNumber与MatchedMerchantReturnLine.OurOrderNumber的值同步。
方式二:使用HasPrincipalKey配置关联(EF Core 5+支持)
如果不想新增字段,可以利用EF Core的HasPrincipalKey特性,将OrderLineContractNumbers的候选键与Receipt的导航属性子属性关联:
modelBuilder.Entity<Receipt>() .HasOne(r => r.OrderNumberDetails) .WithMany(r => r.Receipts) .HasPrincipalKey(on => new { on.OurOrderNumber, on.Item }) // 指定OrderLineContractNumbers的关联键 .HasForeignKey(r => new { OurOrderNumber = r.MatchedMerchantReturnLine.OurOrderNumber, r.Item }) .HasConstraintName("FK_Receipts_OrderLineContractNumbers");
这种方式对EF Core版本有要求,且如果MatchedMerchantReturnLine是可选(可为null)的,可能会在查询或写入时出现问题,优先推荐方式一,更符合EF Core常规外键映射逻辑。
内容的提问来源于stack exchange,提问作者George Duckett

