以下是一个小的EF6程序,用于演示该问题。
简而言之,我们有一个具有显式配置的鉴别器(在这种情况下为“TypeId”的TPH模型)。然后,我们尝试使用该TypeId查询特定的子类型,因为在我们假设的示例中使用“is”运算符也会返回SubAAs,而不仅仅是SubAs。
我们可以修改上面的内容,例如“Where(x => x is SubA &&!(x is SubAA))”,但这显然会在添加SubAB时立即出现问题。通过构建精确过滤器-LINQ-to-Entities辅助方法来自动化这一点显然非常慢,因为该方法必须进行相当数量的反射。更不用说上述生成的SQL非常可怕,因为EF / My SQL Provider无法正确优化它。
现在尝试执行上述操作会导致在查询被实体化时抛出NotSupportedException,这基本上说明由于TypeId不是实体的成员,我不能将其用于过滤。
我去寻找绕过此问题的方法,但我能找到的最好的东西是自动生成“Where(x => x is SubA &&!(x is SubAA))”版本的代码片段,这可能是我解决这个问题的方法。
所以我的问题是:为什么EntityFramework不支持这个?
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();
}
}
简而言之,我们有一个具有显式配置的鉴别器(在这种情况下为“TypeId”的TPH模型)。然后,我们尝试使用该TypeId查询特定的子类型,因为在我们假设的示例中使用“is”运算符也会返回SubAAs,而不仅仅是SubAs。
我们可以修改上面的内容,例如“Where(x => x is SubA &&!(x is SubAA))”,但这显然会在添加SubAB时立即出现问题。通过构建精确过滤器-LINQ-to-Entities辅助方法来自动化这一点显然非常慢,因为该方法必须进行相当数量的反射。更不用说上述生成的SQL非常可怕,因为EF / My SQL Provider无法正确优化它。
现在尝试执行上述操作会导致在查询被实体化时抛出NotSupportedException,这基本上说明由于TypeId不是实体的成员,我不能将其用于过滤。
我去寻找绕过此问题的方法,但我能找到的最好的东西是自动生成“Where(x => x is SubA &&!(x is SubAA))”版本的代码片段,这可能是我解决这个问题的方法。
所以我的问题是:为什么EntityFramework不支持这个?
(x is Thing) && !(x is OtherThing)
级联来解决这个问题,所以那可能是我最终会做的事情。我真正感兴趣的是,为什么这样做会导致异常,是否有任何设计上的原因。 - default0