如何强制实体框架在可以为空的外键上生成SQL内部联接?

时间:2017-06-26 04:26:56

标签: sql-server entity-framework linq linq-to-entities query-performance

更新:EF Team的UserVoice网站上有关于此的请求。 Vote it up

This thread about the Include statement is also related.

为什么这很重要

使用EF6和Linq岩石查询!但如果它涉及一些Join Tables和一个可以为空的外键,那么它就会陷入1000行T-Sql。

如果可以强制使用内部连接,则只能执行10行

例如,EF6项目引用SQL数据库。有一个学生表和一个Tutor表。并非每个学生都有导师,因此Student.TutorId可以为空。

使用T-SQL轻松找到所有学生导师信息:

SELECT s.Name, t.Name FROM Student s JOIN Tutor t ON s.TutorId = t.Id

Linq就是这样:

var result = context.Students
.Where(s => s.TutorId != null)
.Select(s => new { StudentName = s.Name, TutorName = s.Tutor.Name })
.ToList();

但EF6会生成此SQL:

SELECT [Extent1].[Name], 
    CASE WHEN ([Extent2].[FirstName] IS NULL)
       THEN N'' 
    ELSE
       [Extent2].[Name] 
    END AS [C1]
FROM  [dbo].[Student] AS [Extent1]
LEFT OUTER JOIN [dbo].[Tutor] AS [Extent2] ON [Extent1].[TutorId] = [Extent2].[Id]
WHERE [Extent1].[TutorId] IS NOT NULL

感谢Peter asking多年前的GitHub Repository。希望现在有一个更好的答案,而不是放弃Linq。

此{{3}}包含实验源代码。

3 个答案:

答案 0 :(得分:3)

如果在投影后添加非空条件,实体框架将生成内部联接:

var result = context.Students
.Select(s => new { StudentName = s.Name, TutorName = s.Tutor.Name })
.Where(x => x.TutorName != null)
.ToList();

我不知道它为什么会这样。如果EF足够聪明,可以推断x.TutorName != null相当于内连接,我认为它应该与s.TutorId != null相同。

答案 1 :(得分:2)

唯一可靠的方法是,如果您可以构建LINQ查询,使关系从 required end “导航”到可选end 到{{ 1}},我认为这通常不适用。

出于演示目的,如果您像这样编写示例查询

SelectMany

生成的SQL将是这样的

var result = db.Tutors
    .SelectMany(t => t.Students, (t, s) => new { StudentName = s.Name, TutorName = t.Name })
    .ToList();

答案 2 :(得分:1)

在这种情况下,您应该明确地连接表,而不是将tutor称为学生的财产:

var result = (from s in context.Students
             join t in context.Tutors
             on s.TutorId equals t.Id
             select new 
             {
                 StudentName = s.Name, 
                 TutorName = t.Name
             }).ToLost();