EF Core 8配置复杂类型内的外键时迁移报错问题咨询
问题
我正在使用ASP.NET Core 8和EF Core 8开发应用程序,拥有如下所示的AgentAddress值对象:
public sealed class AgentAddress { public string? AddressLine1 { get; private set; } public string? AddressLine2 { get; private set; } public string? PostalCode { get; private set; } public Country Country { get; set; } public Guid CountryId { get; private set; } public Guid? StateOrRegionId { get; private set; } public Guid? CityId { get; private set; } private AgentAddress() { } public AgentAddress(Guid countryId, Guid? stateOrRegionId = null, Guid? cityId = null, string? addressLine1 = null, string? addressLine2 = null, string? postalCode = null) { AddressLine1 = addressLine1; AddressLine2 = addressLine2; PostalCode = postalCode; CountryId = countryId; StateOrRegionId = stateOrRegionId; CityId = cityId; } }
我使用EF Core Fluent API进行配置,配置代码如下:
sealed class AgentConfig : IEntityTypeConfiguration<Core.Agents.Agent> { public void Configure(EntityTypeBuilder<Core.Agents.Agent> builder) { builder.HasKey(s => s.Id); builder.ComplexProperty(e => e.Address, b => { b.Property(p => p.AddressLine1).IsRequired().HasMaxLength(AppConstants.StringLengths.Address); b.Property(p => p.AddressLine2).IsRequired(false).HasMaxLength(AppConstants.StringLengths.Address); b.Property(p => p.PostalCode).IsRequired(false).HasMaxLength(AppConstants.StringLengths.PostalCode); }); builder.HasOne(s => s.Address.Country) .WithMany() .HasForeignKey(s => s.Address.CountryId); } }
尝试将已配置为EF Core 8复杂类型的AgentAddress中的CountryId配置为Country的外键时,添加迁移出现如下错误:
Unable to create a 'DbContext' of type ''. The exception 'The expression 's => s.Address.Country' is not a valid member access expression. The expression should represent a simple property or field access: 't => t.MyProperty'. (Parameter 'memberAccessExpression')' was thrown while attempting to create an instance.
请问该场景是否不可行,还是我的配置存在错误?
解决方案
你的配置存在错误,EF Core目前不支持直接从实体的复杂属性(Complex Property)外层配置嵌套导航属性的关联关系,正确的做法是将关联配置放在复杂属性的内部配置器中:
修正后的配置代码
sealed class AgentConfig : IEntityTypeConfiguration<Core.Agents.Agent> { public void Configure(EntityTypeBuilder<Core.Agents.Agent> builder) { builder.HasKey(s => s.Id); builder.ComplexProperty(e => e.Address, b => { // 原有地址属性配置 b.Property(p => p.AddressLine1).IsRequired().HasMaxLength(AppConstants.StringLengths.Address); b.Property(p => p.AddressLine2).IsRequired(false).HasMaxLength(AppConstants.StringLengths.Address); b.Property(p => p.PostalCode).IsRequired(false).HasMaxLength(AppConstants.StringLengths.PostalCode); // 配置CountryId属性规则 b.Property(p => p.CountryId).IsRequired(); // 在复杂属性内部配置Country关联关系 b.HasOne(p => p.Country) .WithMany() .HasForeignKey(p => p.CountryId); }); } }
关键原因
EF Core的HasOne方法要求传入的表达式必须是直接指向当前配置实体的顶级属性,无法识别嵌套在复杂属性中的导航属性路径。将关联配置放在ComplexProperty的内部配置器中时,EF Core会以当前复杂属性作为上下文,正确解析内部的Country导航属性和CountryId外键,从而生成合法的数据库映射和迁移脚本。
内容的提问来源于stack exchange,提问作者thanzeel

