【问题标题】:Entity Framework 6 big query in inheritanceEntity Framework 6 继承中的大查询
【发布时间】:2018-10-15 11:21:10
【问题描述】:

我有一个继承策略 Table per Type (TPT),其中包含一个抽象类“Task”,其中包含许多具体类(如 30 个类)。 当我尝试显示“待办任务”的工作列表时,向 EF 询问抽象类,或者当我尝试通过 ID 获取通用任务时,EF 会进行 10000 行查询加入每个具体类,结果非常慢。 有没有办法配置 EF 来避免大查询? 在worklist方法中,我只需要抽象类的字段。

这是我的代码:

    public Task GetTaskById(int id) {
        return this.repository.Tasks.Where(t => t.ID == id).FirstOrDefault();
    }

    public IQueryable<Task> GetWorklist() {
        return this.repository.Tasks.Where(t => a.ActivitySate.Code == ActivitySateEnum.TO_DO);
    }

谢谢

【问题讨论】:

  • 你明白抽象类没有表。因此,数据分布在非抽象类的所有表中。我建议您重新考虑您的设计,将公共数据放入所有其他类也具有外键的类中。事实上,这种关系可以在所有其他类继承自的抽象类中建模。
  • 就像你说的,我忘了解释我使用了 Table per Type (TPT) 继承
  • 您的查询可能会返回所有具体类表的结果,因此可以预期多个连接。如果您试图仅从一种具体类型中提取结果(这在您的问题中并不清楚),那么您可以将鉴别器作为查询的一部分,或者仅针对这种类型将不同的 DbSet 添加到您的上下文中。如果您在问题中包含所有相关代码(即您的 DbContext 和类结构)可能会有所帮助。
  • 您需要实际显示您的代码...我的意思是,Tasks 是什么以及存储库的东西。
  • “我使用了每个类型的表 (TPT) 继承”考虑切换到每个层次结构的表 (TPH)。

标签: c# performance entity-framework inheritance


【解决方案1】:

关系数据库不能很好地处理继承的概念。 Several strategies have been invented for entity framework to mock inheritance.

哪种策略最适合您取决于您​​最常执行哪种查询和更新。

假设您有一个类Person,以及两种特定类型的PersonsTeachersStudents。实现继承有两种流行的策略

每种类型的表 (TPT)

每个类都表示在一个单独的表中。在我们的示例中,创建了三个表:Persons 表、Teachers 表,其外键指向 Person,以及 Students 表,其外键指向 @ 的 Person 数据987654331@.

如果您查询:“给我那个...的Persons”,则只需要检查一张表。但是,如果您问:“给我Students 是谁……”,则需要在Persons 表和Students 表之间进行连接。

如果添加/更新/删除一个Student,那么需要更新两个表。

如果将来需要将一列添加到其中一个类中,则只涉及一张表。

添加一种新的Person,如Sponsors 很容易,但是它们必须是Persons 并继承所有Person 列。如果以后你决定 Sponsor 不再是 Person 你就有麻烦了。

如果您对Persons 的询问频率远高于StudentsTeachers,则此方法最适合。如果您经常询问Students with Person data,则不太适合。此外,如果您经常添加/删除/更新Students,请不要使用此方法。

如果您需要创建一个既不是Teacher 也不是StudentPerson,也可以使用此方法,但稍后可能会成为其中之一,或者可能同时成为TeacherStudent

每个创建类 (TPC) 的表

Persons 没有单独的表格。所有Person 属性都在Teachers 表和Students 表中。

查询“...的学生”或“...的老师”将只涉及一张表。但是,查询“...的人”将涉及将从Students 表中检索到的数据与从Teachers 表中检索到的数据连接起来。

添加/删除/更新Student 将始终涉及一个表。

Student 添加一列涉及更改一个表。但是,向Person 添加一列需要同时更改StudentsTeachers 表。

添加一种新的Person,比如JanitorsSponsors 很容易。如果将来Sponsor 不再是Person,这将不是问题。

您不能创建Person,它始终必须是TeacherStudent。一个Student 永远不可能变成一个Teacher,他会变成一个新的Person(这似乎有点讽刺:-)。没有Student 也可以是Teacher

如果您很少要求Persons who ...,但最常要求Students who ...,请使用此方法

结论

为您的继承选择的策略取决于您将如何使用您的表。

您似乎将 30 种 Persons 实现为 TPC(没有单独的 Persons 表)。如果您要求Persons who ...,您的数据库必须连接所有 30 个表的结果。

如果您认为这是迄今为止最常用的查询类型,请考虑将继承策略更改为 TPT。是否应该这样做取决于数据库是否已经填充了大量数据。如果您使用代码优先,您可能会从一个相当空的数据库开始。

【讨论】:

  • OP 以 我有一个继承策略 Table per Type (TPT) 和一个 abstract 类“任务”。这使整个抽象讲座无效,并且也使 TPT 解释如果您查询:“给我...的人”,只需要检查一个表。完全错误。所有表都已连接,这就是具体的 OP 问题。
【解决方案2】:

问题是您使用不返回 IQueryable 的 ba 存储库,因此它不允许 EF 实际使用您拥有的过滤器(您这样做,对吗?)您将返回的数据限制为仅某些字段。

所以,让我们实现实体(这是存储库反模式的 SOOOO 标准)。你去了....为此....它需要加入TPT。这些是 30 个类,这意味着 30 多个表。首先,查询可能没有 10k 行。其次,对于真正复杂的 SQL(您在此处拥有)来说,这是正常且很小的。第三,您自己设置 - 是的,这是提取所有数据所需要的。

解决方案?摆脱 suplus 存储库(您知道,DbContext 是一个存储库),然后根据基本类型制作过滤器,并确保仅将所需的字段投影到匿名类中,以便 EF 进行优化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-13
    • 1970-01-01
    • 2015-11-09
    • 2020-05-07
    • 2014-08-20
    • 1970-01-01
    • 2014-03-15
    • 1970-01-01
    相关资源
    最近更新 更多