如何在 EF Core 中具有两级继承的 LINQ 查询中从基类访问属性

问题描述 投票:0回答:1

我们使用 SQL Server 和 EF Core 将 C# 对象映射到数据库。对象模型有两个抽象级别,如下所示:

public abstract class Animal
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Parrot : Animal
{
    public bool CanSpeak { get; set; }
}

public abstract class Mammal : Animal
{
    public bool HasPouch { get; set; }
}

public class Dog : Mammal
{
    public bool HasTail { get; set; }
}

public class Cat : Mammal
{
    public double MaxHeight { get; set; }
}

public class Horse : Mammal
{
    public string Breed { get; set; }
    public int RacingSpeed { get; set; }
}

DbContext
继承类的主要部分:

public DbSet<Animal> Animals { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Animal>()
        .ToTable("Animals")
        .HasDiscriminator<string>("AnimalType")
        .HasValue<Parrot>("Parrot")
        .HasValue<Dog>("Dog")
        .HasValue<Cat>("Cat")
        .HasValue<Horse>("Horse");

    // Additional configurations if needed
}

现在我将展示 3 种类型的 LINQ 查询,其中前两种可以工作,但最后一种会导致运行时错误(

IvalidOperationException
- 类似于:LINQ 表达式 'DbSet<
Animal
>().OfType<
 Mammal
>()'无法翻译)。

此查询从层次结构的最基础 abstract 类中查询属性,该类在

Animals
表的 EF 配置中使用:

List<Animal> list = context.Animals
    .Where(x => x.Name == "Max")
    .ToList();

没问题。

这个需要从具体类查询属性并且工作得很好:

List<Animal> catsUnder50cm = context.Animals
    .Where(x => x is Cat && (x as Cat).MaxHeight <= 50)
    .ToList();

然而,对于这个,虽然编译正常,但我得到了上面提到的运行时错误:

List<Animal> marsupials = context.Animals
    .Where(x => x is Mammal && (x as Mammal).HasPouch)
    .ToList();

我认为这是因为

Mammal
是一个抽象类。我可以这样重写:

List<Animal> marsupials2 = context.Animals
    .Where(x => (x is Dog && (x as Dog).HasPouch) || (x is Cat && (x as Cat).HasPouch) || (x is Horse && (x as Horse).HasPouch) )
    .ToList();

但是正如您所看到的,查询同时变得非常复杂和丑陋。从

Mammal
继承的类越多,查询就越复杂。这正是我们的情况。

问题是:

是否可以在 LINQ 查询中使用不属于模型一部分的“中间”抽象类的属性?

如果没有,我们能否以某种方式将该抽象类“添加”到 EF 数据模型中?

Mammal
数据类型应映射到
Animals
的同一个表。这可能吗?

c# entity-framework linq inheritance tph
1个回答
1
投票

εὕρηκᾰ

我想我找到了解决方案。不过我不太喜欢它。

作为解决方法,我需要将“中间”抽象类映射到同一个表。并将其新属性 (

HasPouch
) 映射到同一列。

基本上,您应该在

DbContext
配置中执行类似的操作:

EntityTypeBuilder<Mammal> mammalBuilder = modelBuilder.Entity<Mammal>();
mammalBuilder.ToTable("Animals");
mammalBuilder.Property(x => x.HasPouch).HasColumnName("HasPouch");
© www.soinside.com 2019 - 2024. All rights reserved.