You need to enable JavaScript to run this app. EF Core TPH模式下不使用中间类能否直接访问共享列?
优惠活动
大模型
产品
解决方案
定价
更多

EF Core TPH模式下不使用中间类能否直接访问共享列?

解决方案

你之前调用EF.Property报错的核心原因是:EF Core元数据系统中,基类ActionBase没有关联Message属性的映射信息,直接调用会触发属性不存在的异常。以下三种方案都可以在不修改现有继承结构、不改动实体属性定义的前提下实现需求:


方案1:给基类添加影子属性(最推荐)

仅需在OnModelCreating中为基类注册Message影子属性,完全不破坏现有实体的验证规则和结构:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    // 给基类添加Message影子属性,无需修改实体类代码
    builder.Entity<ActionBase>().Property<string>("Message");

    // 原有其他配置保持不变
    builder.Entity<ActionA>().HasData(new ActionA()
    {
        Id = 1,
        Message = "A"
    });
    builder.Entity<ActionB>().HasData(new ActionB()
    {
        Id = 2,
        Message = "B"
    });
    builder.Entity<ActionC>().HasData(new ActionC()
    {
        Id = 3,
        SomethingElse = "C"
    });
}

配置完成后,你之前注释的Message = EF.Property<string>(a, "Message")就可以正常运行。由于ActionBase实体类本身没有定义Message属性,也不会出现误给ActionC实例赋值的问题。


方案2:使用原始SQL查询

如果不想修改模型配置,可以直接通过FromSqlRaw指定查询共享列,后续投影即可正常访问:

var actions = await context.Actions
    .FromSqlRaw("SELECT Id, Message, Discriminator FROM Actions")
    .Select(a => new
    {
        Id = a.Id,
        Message = EF.Property<string>(a, "Message")
    })
    .ToListAsync();

该方案适合临时查询场景,无需改动模型配置,注意SQL语句中的列名要和数据库实际生成的列名保持一致即可。


方案3:封装类型判断逻辑为扩展

如果既不想改配置也不想写原生SQL,可以把冗余的类型转换逻辑封装为可复用的表达式,避免每次查询重复写类型判断代码:

public static class ActionQueryExtensions
{
    public static IQueryable<object> SelectWithMessage(this IQueryable<ActionBase> query)
    {
        return query.Select(a => new
        {
            Id = a.Id,
            Message = (a as ActionA).Message ?? (a as ActionB).Message
        });
    }
}

// 调用时直接复用扩展
var actions = await context.Actions
    .SelectWithMessage()
    .ToListAsync();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.13 07:45:01