Entity Framework中如何将子类IQueryable转换为父类类型?
Entity Framework中子类IQueryable转父类类型的正确姿势
首先说结论:根本不需要什么专门的反向方法。因为C#里子类本身就是父类的实例,EF的查询体系直接支持这种类型转换,完全没必要像你那样在内存里复制集合。
先吐槽下你写的“错误方式”:你先把Student数据全加载到内存(ToList()那一步),再手动创建新List转类型,这纯纯是做无用功——既浪费内存,又没用到EF的查询优化能力。
正确实现方式
直接用下面两种方法就行,都不用额外折腾集合:
方法1:Cast直接转类型
因为Student继承自User,IQueryable<Student>可以直接Cast成IQueryable<User>,EF会正确解析这个转换,不会乱改SQL:
public class User {} public class Teacher : User {} public class Student : User {} public class Session { public Guid Id { get; set; } public Teacher Teacher { get; set; } public IEnumerable<Student> Students { get; set; } } public IQueryable<User> GetStudentsAsUsers(Guid sessionId) { return context.Sessions .Where(session => session.Id == sessionId) .SelectMany(session => session.Students) // 注意这里要用SelectMany,不然会返回嵌套的集合 .Cast<User>(); }
方法2:Select投影转换
也可以用Select直接把Student强转成User,效果和Cast一模一样:
public IQueryable<User> GetStudentsAsUsers(Guid sessionId) { return context.Sessions .Where(session => session.Id == sessionId) .SelectMany(session => session.Students) .Select(student => (User)student); }
要是你确实要返回内存集合
就算你需要IEnumerable<User>,也不用手动AddRange,直接Cast完ToList就行:
public IEnumerable<User> GetStudentsAsUsers(Guid sessionId) { return context.Sessions .Where(session => session.Id == sessionId) .SelectMany(session => session.Students) .Cast<User>() .ToList(); }
为啥你的写法不好?
ToList()会把所有Student数据拉到本地内存,再转换,完全没必要——EF可以在查询层面直接处理类型转换,不用提前加载数据。- 手动创建List再
AddRange是冗余操作,Cast<User>()直接就能把IEnumerable<Student>转成IEnumerable<User>。
额外提一句
不管你的继承是EF里的TPH、TPT还是TPC映射方式,这种转换都有效,EF能正确处理继承关系的查询逻辑,生成的SQL不会有多余内容。
内容的提问来源于stack exchange,提问作者HEDGEWIZARDS
相关产品推荐
相关产品推荐

