EF Code First查询时如何仅加载SpecializedPerson实体而非其子类
这个问题在Entity Framework的TPH(Table Per Hierarchy)继承策略下太常见了——默认查询父类SpecializedPerson时,EF会自动把所有派生类(比如你的SuperSpecializedPerson)的记录也包含进来,因为它生成的SQL里鉴别器条件是父类+所有子类的集合。要只获取SpecializedPerson本身的实例,同时解决DTO重复的问题,这里有几个实用方案:
1. 类型安全的过滤查询
直接在查询中通过类型判断过滤,EF会自动转换成对应的鉴别器筛选条件,生成的SQL就是你想要的仅匹配SpecializedPerson的版本:
var onlySpecializedPeople = dbContext.SpecializedPeople .Where(p => p.GetType() == typeof(SpecializedPerson)) .ToList();
如果你想直接操作鉴别器字段(默认字段名为Discriminator),也可以这么写(但不如上面的类型安全):
var onlySpecializedPeople = dbContext.SpecializedPeople .Where(p => EF.Property<string>(p, "Discriminator") == "SpecializedPerson") .ToList();
2. 在仓储层封装专用方法
既然你已经为实体实现了仓储,可以在SpecializedPerson的仓储中添加一个专门获取“仅父类实例”的方法,把过滤逻辑封装起来,避免业务层重复代码:
public class SpecializedPersonRepository : ISpecializedPersonRepository { private readonly YourAppDbContext _dbContext; public SpecializedPersonRepository(YourAppDbContext dbContext) { _dbContext = dbContext; } // 原有查询方法 public IEnumerable<SpecializedPerson> GetAll() { return _dbContext.SpecializedPeople.ToList(); } // 新增:仅获取父类实例 public IEnumerable<SpecializedPerson> GetOnlyBaseInstances() { return _dbContext.SpecializedPeople .Where(p => p.GetType() == typeof(SpecializedPerson)) .ToList(); } }
3. 解决DTO重复的问题
你提到合并列表后SuperSpecializedPerson出现两次,根源就是之前查询SpecializedPerson时把子类也拉了进来,转换成DTO后和单独查询的SuperSpecializedPersonDTO重复。用上面的过滤方法获取纯父类实例后,再分别转换合并就不会有重复了:
// 查询并转换SuperSpecializedPerson为对应DTO var superDtoList = _superSpecializedPersonRepo.GetAll() .ProjectTo<SuperSpecializedPersonDto>(_mapper.ConfigurationProvider) .ToList(); // 查询仅父类实例并转换为DTO var specializedDtoList = _specializedPersonRepo.GetOnlyBaseInstances() .ProjectTo<SpecializedPersonDto>(_mapper.ConfigurationProvider) .ToList(); // 合并为统一的父类DTO列表(如果需要) var combinedDtoList = specializedDtoList .Concat(superDtoList.Cast<SpecializedPersonDto>()) .ToList();
额外说明
这个方案的扩展性很好——未来如果新增第二个继承SpecializedPerson的子类,p.GetType() == typeof(SpecializedPerson)的判断依然有效,只会匹配刚好是SpecializedPerson类型的实例,不会包含任何派生类,不用修改过滤逻辑就能适配新的继承关系。
内容的提问来源于stack exchange,提问作者Philippe

