如何强制 Entity Framework 在可为空的外键上生成 SQL 内连接?
How can Entity Framework be forced to generate a SQL Inner Join on a nullable Foreign Key?
更新: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 数据库。有一个学生 table 和一个导师 table。并非每个学生都有导师,因此 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 for asking 多年前的这件事。希望现在有比放弃 Linq 更好的答案。
这个 GitHub Repository 有用于实验的源代码。
在这种情况下,您应该显式连接表,而不是将导师作为学生的 属性:
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();
Entity Framework 如果在 投影后添加非空条件 将生成内部联接:
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
.
相同
唯一可靠的方法是,如果您可以构建 LINQ 查询,使关系 "navigated" 从 所需的结束 到 可选的 end 到 SelectMany
,我想这使得它不普遍适用。
出于演示目的,如果您像这样编写示例查询
var result = db.Tutors
.SelectMany(t => t.Students, (t, s) => new { StudentName = s.Name, TutorName = t.Name })
.ToList();
生成的SQL会是这样的
SELECT
[Extent1].[Id] AS [Id],
[Extent2].[Name] AS [Name],
[Extent1].[Name] AS [Name1]
FROM [dbo].[Tutors] AS [Extent1]
INNER JOIN [dbo].[Students] AS [Extent2] ON [Extent1].[Id] = [Extent2].[TutorId]
更新: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 数据库。有一个学生 table 和一个导师 table。并非每个学生都有导师,因此 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 for asking 多年前的这件事。希望现在有比放弃 Linq 更好的答案。
这个 GitHub Repository 有用于实验的源代码。
在这种情况下,您应该显式连接表,而不是将导师作为学生的 属性:
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();
Entity Framework 如果在 投影后添加非空条件 将生成内部联接:
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
.
唯一可靠的方法是,如果您可以构建 LINQ 查询,使关系 "navigated" 从 所需的结束 到 可选的 end 到 SelectMany
,我想这使得它不普遍适用。
出于演示目的,如果您像这样编写示例查询
var result = db.Tutors
.SelectMany(t => t.Students, (t, s) => new { StudentName = s.Name, TutorName = t.Name })
.ToList();
生成的SQL会是这样的
SELECT
[Extent1].[Id] AS [Id],
[Extent2].[Name] AS [Name],
[Extent1].[Name] AS [Name1]
FROM [dbo].[Tutors] AS [Extent1]
INNER JOIN [dbo].[Students] AS [Extent2] ON [Extent1].[Id] = [Extent2].[TutorId]