下面是一个小的 EF6 程序来演示这个问题。
public abstract class Base
{
public int Id { get; set; }
public abstract int TypeId { get; }
}
public class SubA : Base
{
public override int TypeId => 1;
}
public class SubAA : SubA
{
public override int TypeId => 2;
}
public class SubB : Base
{
public override int TypeId => 3;
}
public class SubC : Base
{
public override int TypeId => 4;
}
public class DevartContext : DbContext
{
public virtual DbSet<Base> Bases { get; set; }
public DevartContext()
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Base>()
.Map<SubA>(x => x.Requires(nameof(SubA.TypeId)).HasValue(1))
.Map<SubAA>(x => x.Requires(nameof(SubAA.TypeId)).HasValue(2))
.Map<SubB>(x => x.Requires(nameof(SubB.TypeId)).HasValue(3))
.Map<SubC>(x => x.Requires(nameof(SubC.TypeId)).HasValue(4));
}
}
public class Program
{
public static void Main(string[] args)
{
using (DevartContext ctx = new DevartContext())
{
// prevent model-changes from wrecking the test
ctx.Database.Delete();
ctx.Database.Create();
var result = ctx.Bases.Where(x => x.TypeId == 1);
// throws on materialization, why?
foreach (var entry in result)
{
Console.WriteLine(entry);
}
}
Console.ReadLine();
}
}
它的要点是:我们有一个带有显式配置的鉴别器的 TPH 模型(TypeId
在这种情况下)。然后我们尝试使用该 TypeId 查询特定的子类型,因为is
在我们的假设示例中使用运算符也会返回 SubAAs,而不仅仅是 SubAs。
我显然可以将上面的内容修改为类似的Where(x => x is SubA && !(x is SubAA))
内容,但这显然会在我添加 SubAB 后立即中断,并且通过构建exact-filter-linq-to-entities-helper-method 来自动执行此操作显然非常慢,因为该方法有做大量的反思。更不用说上面生成的 SQL 太可怕了,因为 EF/My SQL Provider 没有正确优化它。
现在尝试执行上述操作会导致在NotSupportedException
查询具体化时抛出,这基本上表明因为TypeId
不是实体的成员,所以我不能使用它进行过滤。
我四处寻找绕过这个问题的方法,但我能找到的最好的东西是一个自动生成Where(x => x is SubA && !(x is SubAA))
版本以解决问题的片段,这很可能是我必须做的才能解决这个问题。
所以我的问题是:为什么 EntityFramework 不支持这个?