【发布时间】:2015-07-27 11:04:50
【问题描述】:
我们使用的是 EF 6.1。尽管 v4 有所改进,但通常需要帮助 EF 决定如何更高效地生成 SQL。通常有助于在我们的案例中使用 LINQ 并指定连接。
但是,现在我遇到了一个我不知道该怎么做的情况(除了完全绕过 EF):
return db.Testlets.Include("TestTasks.TestQuestions.TestAnswers")
.Include("TestTasks.TestQuestions.TestQuestionCriterionGroups.TestQuestionCriterions")
.Include("TestTasks.TestQuestions.Question.Answers")
.Where(x => x.TestId == testId && x.ShownOn.HasValue)
.ToList();
这会产生非常低效的代码。事实上,如果 EF 产生这样的东西就足够了,而且最好:
SELECT *
FROM TestLet TL
INNER JOIN TestTask TT ON TL.Guid = TT.TestletId
INNER JOIN TestQuestion TQ ON TT.Guid = TQ.TestTaskId
INNER JOIN TestAnswer TA ON TQ.Guid = TA.TestQuestionId
LEFT OUTER JOIN TestQuestionCriterionGroup TQCG ON TQCG.TestQuestionId = TQ.Guid
LEFT OUTER JOIN TestQuestionCriterion TQC ON TQCG.Guid = TQC.TestQuestionCriterionGroupId
INNER JOIN Question Q ON TQ.QuestionId = Q.QuestionId AND Q.IsActive = 1
INNER JOIN Answer A ON Q.QuestionId = A.QuestionId AND A.IsActive = 1
WHERE
TL.TestId='59ADFB3F-16A6-46E0-8054-7F6E83414DC9'
AND TL.ShownOn IS NOT NULL
我发现下面的代码(最后没有包含)生成了上面的 SQL,但只选择了 testlet 列(没有应用包含,因为它们不存在,因此没有映射到 EF 实体) 并且我需要急切地加载整个层次结构。当我最后添加包含时,生成的 SQL 又很糟糕而且非常慢:
(from tl in
db.Testlets.Where(tl => tl.TestId == testId && tl.ShownOn.HasValue)
from tt in db.TestTasks.Where(tt => tl.Guid == tt.TestletId)
from tq in db.TestQuestions.Where(tq => tt.Guid == tq.TestTaskId)
from ta in db.TestAnswers.Where(ta => tq.Guid == ta.TestQuestionId)
from q in db.Questions.Where(q => tq.QuestionId == q.Id)
from a in db.Answers.Where(a => q.Id == a.QuestionId)
from tqcg in
db.TestQuestionCriterionGroups.Where(tqcg => tq.Guid == tqcg.TestQuestionId).DefaultIfEmpty()
from tqc in
db.TestQuestionCriterions.Where(tqc => tqcg.Guid == tqc.TestQuestionCriterionGroupId)
.DefaultIfEmpty()
select tl).Include("TestTasks.TestQuestions.TestAnswers")
.Include("TestTasks.TestQuestions.TestQuestionCriterionGroups.TestQuestionCriterions")
.Include("TestTasks.TestQuestions.Question.Answers")
有人知道如何编写高效且结果正确的 linq2sql o entity2sql 代码吗?还是只有在这种更复杂的情况下放弃 EF 的方法?如果是这样,如何以最简单的方式将映射回 EF 结构(从带有上述连接的 SQL)?
如果有人想了解更多关于如何进行左连接的信息:https://msdn.microsoft.com/en-us/library/bb397895.aspx
以及为什么在开头的查询中指定时包含不起作用: http://blogs.msdn.com/b/alexj/archive/2009/06/02/tip-22-how-to-make-include-really-include.aspx
更新:生成 sql 的要点:https://gist.github.com/Ondrashx/d0347fc807f0f7fbdf46
【问题讨论】:
-
您确实应该包含生成的 SQL,它可以通过连接到
DbContext.Database.Log轻松检索。 -
您是否考虑过返回一个匿名对象,其中包含每个相关表中的相关字段。
-
@B2K 感谢您的建议。我想像现在一样使用 EF 实体,因为其他代码依赖于它们,我希望这种变化的影响尽可能小。
-
@Patryk 它很长,所以把它放在这里可能不是一个好主意,我会把它贴在某个地方并在此处添加链接
-
我建议使用匿名对象的原因是因为 sql 查询并非旨在返回分层结果。当 EF 需要从表格查询结果重新创建它时,这会导致固有的低效率。
标签: c# sql linq entity-framework optimization