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

如何使用EF Core 7 TPC配置获取嵌套结构数据

问题:EF Core中TPC继承下PossibleAnswerCategory的PossibleAnswers集合为空的解决办法

我有如下抽象基类:

public abstract class PossibleAnswerBase
{
    public Uuid Id { get; protected set; }
    public string Title { get; protected set; }

    #region Navigation

    public Uuid QuestionId { get; protected set; }
    public Question Question { get; }
        
    public Uuid? ParentId { get; protected set; }
    public PossibleAnswerCategory Parent { get; }
        
    #endregion

    protected PossibleAnswerBase()
    {
    }
}

两个继承类,其中PossibleAnswerCategory支持嵌套结构:

public class PossibleAnswerCategory : PossibleAnswerBase
{
    public ICollection<PossibleAnswerBase> PossibleAnswers { get; private set; }

    public PossibleAnswerCategory(Uuid id, Uuid? parentId, string title)
    {
        Id = id;
        Title = title;
        ParentId = parentId;
    }

    [UsedImplicitly]
    private PossibleAnswerCategory()
    {
    }
}

public class PossibleAnswer : PossibleAnswerBase
{
    public PossibleAnswer(Uuid id, string title, Uuid? parentId)
    {
        Id = id;
        Title = title;
        ParentId = parentId;
    }
}

上下文配置如下:

private void MapQuestionPossibleAnswerBase(ModelBuilder modelBuilder)
{
    var builder = modelBuilder.Entity<PossibleAnswerBase>()
        .UseTpcMappingStrategy();
    builder.HasKey(r => r.Id);

    builder.Property(r => r.Id).HasColumnName("Id");
    builder.Property(r => r.Title).HasColumnName("Title");
    builder.Property(r => r.QuestionId).HasColumnName("QuestionId");
    builder.Property(r => r.ParentId).HasColumnName("ParentId");
            
    builder.HasOne(pa => pa.Question)
        .WithMany(q => q.PossibleAnswers)
        .HasForeignKey(pa => pa.QuestionId);

    builder.Navigation(pa => pa.Question).AutoInclude();
}

private void MapQuestionPossibleAnswerCategory(ModelBuilder modelBuilder)
{
    var builder = modelBuilder.Entity<PossibleAnswerCategory>()
        .UseTpcMappingStrategy().ToTable("violation_categories");

    builder.HasMany(w => w.PossibleAnswers)
        .WithOne(w => w.Parent)
        .HasForeignKey(w => w.ParentId);
}

private void MapQuestionPossibleAnswers(ModelBuilder modelBuilder)
{
    var paBuilder = modelBuilder.Entity<PossibleAnswer>()
        .UseTpcMappingStrategy().ToTable("violations");
}

现在查询时PossibleAnswerCategory的PossibleAnswers集合始终为空,请问如何配置上下文以正确填充该集合?


解决方案

1. 初始化集合属性

EF Core需要集合实例来填充关联数据,务必在PossibleAnswerCategory的所有构造函数中初始化PossibleAnswers:

public class PossibleAnswerCategory : PossibleAnswerBase
{
    public ICollection<PossibleAnswerBase> PossibleAnswers { get; private set; }

    public PossibleAnswerCategory(Uuid id, Uuid? parentId, string title)
    {
        Id = id;
        Title = title;
        ParentId = parentId;
        PossibleAnswers = new List<PossibleAnswerBase>();
    }

    [UsedImplicitly]
    private PossibleAnswerCategory()
    {
        PossibleAnswers = new List<PossibleAnswerBase>();
    }
}

2. 修正TPC关联配置

TPC策略下基类与子类表独立,需明确关联的实体类型并配置导航加载:

private void MapQuestionPossibleAnswerCategory(ModelBuilder modelBuilder)
{
    var builder = modelBuilder.Entity<PossibleAnswerCategory>()
        .ToTable("violation_categories"); // TPC策略已在基类配置,子类无需重复声明

    // 明确关联目标为PossibleAnswerBase,设置删除行为
    builder.HasMany(c => c.PossibleAnswers)
        .WithOne(pa => pa.Parent)
        .HasForeignKey(pa => pa.ParentId)
        .OnDelete(DeleteBehavior.Restrict);

    // 开启自动包含,避免每次查询手动Include
    builder.Navigation(c => c.PossibleAnswers).AutoInclude();
}

3. 补全基类导航属性映射

在基类配置中明确Parent导航的关联关系:

private void MapQuestionPossibleAnswerBase(ModelBuilder modelBuilder)
{
    var builder = modelBuilder.Entity<PossibleAnswerBase>()
        .UseTpcMappingStrategy();
    builder.HasKey(r => r.Id);

    // ... 原有属性配置 ...
            
    builder.HasOne(pa => pa.Question)
        .WithMany(q => q.PossibleAnswers)
        .HasForeignKey(pa => pa.QuestionId);

    // 配置Parent与PossibleAnswers的反向关联
    builder.HasOne(pa => pa.Parent)
        .WithMany(c => c.PossibleAnswers)
        .HasForeignKey(pa => pa.ParentId)
        .IsRequired(false); // ParentId可空,关联设为非必需

    builder.Navigation(pa => pa.Question).AutoInclude();
}

4. 查询时显式Include(可选)

若自动包含未生效,可在查询时手动指定加载集合:

var categories = await _context.Set<PossibleAnswerCategory>()
    .Include(c => c.PossibleAnswers)
    .ToListAsync();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 19:10:34