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

EF Core 7.0.8中SizeUnitMeasure/WeightUnitMeasure导航属性未填充问题

EF Core 7.0.8中UnitMeasure导航属性无法填充的问题

我在C#控制台应用中复现EF Core 7.0.8的一个问题,使用了简单模型。

查询代码:

using ConsoleApp1;
using Microsoft.EntityFrameworkCore;

using var db = new ProductContext();

Console.WriteLine("Get product df86dd7f-727d-462f-93e8-6a9caf577a42");
var query = db.Products
    .Include(_ => _.ProductModel)
    .Include(_ => _.SizeUnitMeasure)
    .Include(_ => _.WeightUnitMeasure)
    .Where(_ => _.ObjectId == Guid.Parse("df86dd7f-727d-462f-93e8-6a9caf577a42"));

var queryString = query.ToQueryString();
var product = query.First();

Console.WriteLine($"Product name: {product.Name}");

ProductContext类代码:

public class ProductContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    override protected void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("myConnectionString");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Product>()
            .ToTable("Product")
            .HasKey(_ => _.Id);

        modelBuilder.Entity<Product>()
            .Property(_ => _.Id)
            .HasColumnName("ProductId");

        modelBuilder.Entity<Product>()
            .HasOne(_ => _.SizeUnitMeasure)
            .WithMany()
            .HasForeignKey(_ => _.SizeUnitMeasureCode);

        modelBuilder.Entity<Product>()
            .HasOne(_ => _.WeightUnitMeasure)
            .WithMany()
            .HasForeignKey(_ => _.WeightUnitMeasureCode);

        modelBuilder.Entity<UnitMeasure>()
            .ToTable("UnitMeasure")
            .HasKey(_ => _.UnitMeasureCode);

        modelBuilder.Entity<ProductModel>()
            .ToTable("ProductModel")
            .HasKey(_ => _.Id);

        modelBuilder.Entity<ProductModel>()
            .Property(_ => _.Id)
            .HasColumnName("ProductModelID");
    }
}

实体类代码:

public class Product
{
    public int Id { get; private set; }
    public Guid ObjectId { get; private set; }
    public string? Name { get; private set; }
    public ProductModel? ProductModel { get; private set; }
    public string? SizeUnitMeasureCode { get; private set; }
    public UnitMeasure? SizeUnitMeasure { get; set; }
    public string? WeightUnitMeasureCode { get; private set; }
    public UnitMeasure? WeightUnitMeasure { get; set; }
}

public class ProductModel
{
    public int Id { get; private set; }
    public Guid ObjectId { get; private set; }
    public string? Name { get; private set; }
}

public class UnitMeasure
{
    public string? UnitMeasureCode { get; private set; }
    public Guid ObjectId { get; private set; }
    public string? Name { get; private set; }
}

问题现象:Product实体的SizeUnitMeasure和WeightUnitMeasure导航属性查询后未被填充,但ProductModel属性正常填充。EF生成的SQL查询语句看似正确,关联查询也返回了正确结果:

SELECT [p].[ProductId], [p].[Name], [p].[ObjectId], [p].[ProductModelId], [p].[SizeUnitMeasureCode], [p].[WeightUnitMeasureCode], [p0].[ProductModelID], [p0].[Name], [p0].[ObjectId], [u].[UnitMeasureCode], [u].[Name], [u].[ObjectId], [u0].[UnitMeasureCode], [u0].[Name], [u0].[ObjectId]
FROM [Product] AS [p]
LEFT JOIN [ProductModel] AS [p0] ON [p].[ProductModelId] = [p0].[ProductModelID]
LEFT JOIN [UnitMeasure] AS [u] ON [p].[SizeUnitMeasureCode] = [u].[UnitMeasureCode]
LEFT JOIN [UnitMeasure] AS [u0] ON [p].[WeightUnitMeasureCode] = [u0].[UnitMeasureCode]
WHERE [p].[ObjectId] = 'df86dd7f-727d-462f-93e8-6a9caf577a42'

已尝试的操作:修改Product类中UnitMeasure属性的setter为公共无效;将SizeUnitMeasureCode列重命名为SizeUnitMeasureUnitMeasureCode并移除HasForeignKey配置,问题依旧。


问题原因分析

  1. UnitMeasure主键的私有setter限制
    UnitMeasure的主键UnitMeasureCode使用私有setter,EF Core映射查询结果时,需要正确设置主键值来识别实体并关联导航属性。虽然ProductModel主键也是私有,但它是数值类型(int),EF可通过默认构造逻辑初始化;而字符串类型的主键私有setter会导致EF无法正确赋值,进而无法将查询到的UnitMeasure实例关联到Product的对应导航属性。

  2. 同类型多导航属性的映射歧义
    同时Include两个指向UnitMeasure的导航属性时,EF需要区分两个不同实例。如果主键无法正确设置,EF无法识别这两个实例的差异,导致无法匹配到对应的导航属性。

解决方案

方案1:将UnitMeasure主键的setter改为公共

修改UnitMeasure类的主键属性,开放setter:

public class UnitMeasure
{
    public string? UnitMeasureCode { get; set; }
    public Guid ObjectId { get; private set; }
    public string? Name { get; private set; }
}

这样EF可以正常设置主键值,完成实体关联。

方案2:为UnitMeasure配置可被EF识别的私有构造函数

若要保持setter私有,可添加私有无参构造函数,并在模型配置中声明:

public class UnitMeasure
{
    private UnitMeasure() { }

    public string? UnitMeasureCode { get; private set; }
    public Guid ObjectId { get; private set; }
    public string? Name { get; private set; }
}

在OnModelCreating中添加配置:

modelBuilder.Entity<UnitMeasure>()
    .ToTable("UnitMeasure")
    .HasKey(_ => _.UnitMeasureCode)
    .HasConstructor();

该方式让EF可以通过反射调用私有构造函数初始化实体。

方案3:验证数据库关联值的有效性

手动执行生成的SQL,确认返回的u.UnitMeasureCode和u0.UnitMeasureCode不为null,且与UnitMeasure表中的UnitMeasureCode完全匹配(注意字符串大小写、空格等细节)。

内容的提问来源于stack exchange,提问作者ngruson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 22:02:26