3

下面是一个小的 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 不支持这个?

4

1 回答 1

1

这个灵魂像你希望的那样工作得很好,不会改变任何东西^^“永远不要改变正在运行的系统”:)

您可以使用枚举而不是整数,这使您的代码更加安全!

static void Main(string[] args)
{
  using (DevartContext ctx = new DevartContext())
  {
    // prevent model-changes from wrecking the test
    ctx.Database.Delete();
    ctx.Database.Create();
    ctx.Bases.Add(new SubA());
    ctx.Bases.Add(new SubAA());
    ctx.Bases.Add(new SubB());

    ctx.SaveChanges();

    var result = ctx.Bases.Where(x => x.TypeId == 1);
    // throws on materialization, why?
    foreach (var entry in result)
    {
      Console.WriteLine(entry);
    }
  }
   Console.ReadLine();
  }


public abstract class Base
{
  public int Id { get; set; }

  public virtual int TypeId { get; protected set; } 
}
public class SubA : Base
{
  public override int TypeId { get;protected set; } = 1;
}
public class SubAA : SubA
{
  public override int TypeId { get; protected set; } = 2;
}
public class SubB : Base
{
  public override int TypeId { get; protected set; } = 3;
}
public class SubC : Base
{
  public override int TypeId { get; protected set; } = 4;
}

public class DevartContext : DbContext
{
  public DbSet<Base> Bases { get; set; }

  public DevartContext()
  {
  }
}

数据库中的结果:

Id  TypeId  Discriminator
1   1       SubA
2   2       SubAA
3   3       SubB
于 2016-05-25T11:04:40.940 回答