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
相关产品推荐
相关产品推荐

