【发布时间】:2019-04-06 17:39:17
【问题描述】:
我是 EF 菜鸟(就像我今天刚开始一样,我只使用过其他 ORM),我正在经历一场烈火的洗礼。
有人要求我提高另一个开发人员创建的查询的性能:
var questionnaires = await _myContext.Questionnaires
.Include("Sections")
.Include(q => q.QuestionnaireCommonFields)
.Include("Sections.Questions")
.Include("Sections.Questions.Answers")
.Include("Sections.Questions.Answers.AnswerMetadatas")
.Include("Sections.Questions.Answers.SubQuestions")
.Include("Sections.Questions.Answers.SubQuestions.Answers")
.Include("Sections.Questions.Answers.SubQuestions.Answers.AnswerMetadatas")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.AnswerMetadatas")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.AnswerMetadatas")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.AnswerMetadatas")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers")
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.AnswerMetadatas")
.Where(q => questionnaireIds.Contains(q.Id))
.ToListAsync().ConfigureAwait(false);
快速的网上冲浪告诉我,如果您运行多个级别,Include() 会导致 cols * rows 产品和较差的性能。
我在 SO 上看到了一些有用的答案,但它们的复杂示例有限,而且我无法找出重写上述内容的最佳方法。
该部分的多次重复 -“Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers...”对我来说看起来很可疑,就像它可以单独完成然后发出另一个查询一样,但我不知道如何构建它,或者这种方法是否会提高性能。
问题:
如何将此查询重写为更合理的查询以提高性能,同时确保最终结果集相同?
给定最后一行:
.Include("Sections.Questions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.SubQuestions.Answers.AnswerMetadatas")
为什么我需要所有的中间线? (我猜是因为有些连接可能不是左连接?)
EF 版本信息:包 id="EntityFramework" version="6.2.0" targetFramework="net452"
我意识到这个问题有点垃圾,但我试图从一无所知的角度尽快解决。
编辑
在考虑了半天之后,感谢 StuartLC 的建议,我想出了一些选择:
差 - 拆分查询,以便它执行多次往返以获取数据。这可能会为用户提供稍慢的体验,但会阻止 SQL 超时。 (这并不比仅仅增加 EF 命令超时好多少)。
好 - 将子表上的聚集索引更改为由其父表的外键聚集(假设您没有很多插入操作)。
好 - 将代码更改为仅查询前几个级别并延迟加载(单独的数据库命中)低于此的任何内容,即删除除前几个包含之外的所有内容,然后更改 ICollections - Answers.SubQuestions、Answers.AnswerMetadatas、和 Question.Answers 都是虚拟的。据推测,使这些虚拟化的不利之处在于,如果应用程序中的任何(其他)现有代码期望这些 ICollection 属性被预先加载,您可能必须更新该代码(即,如果您希望/需要它们立即在该代码中加载)。我将进一步研究这个选项。进一步编辑 - 不幸的是,如果由于自引用循环而需要序列化响应,这将不起作用。
非常重要 - 手动编写一个 sql 存储的 proc/view 并构建一个指向它的新 EF 对象。
长期
显而易见、最好但最耗时的选项 - 重写应用程序设计,因此它不需要在单个 api 调用中使用整个数据树,或者使用以下选项:
重写应用程序以以 NoSQL 方式存储数据(例如,将对象树存储为 json,因此没有连接)。正如 Stuart 所提到的,如果您需要以其他方式(通过问卷 ID 以外的方式)过滤数据,这不是一个好的选择,您可能需要这样做。另一种选择是根据需要部分存储 NoSQL 样式和部分关系。
【问题讨论】:
-
@immirza - 谢谢,虽然我不明白那个代码,因为他重用了“var customers”,这会是编译错误吗?
-
不,编译器自动确定类型。一个例子,var i = 10;和int i = 10;在功能上是等效的。
-
@immirza 我的意思是 - 他多次声明同一个变量,即 var customers = ... var customers =... var customers = ... 我猜 var 不应该是在第一个之后。我试试看。
标签: c# entity-framework entity-framework-6