如何在Entity Framework Core中构建多个左连接查询

考虑我有以下实体:

public class Root { public long Id { get; set; } } public class School : Root { public long StudentId { get; set; } public Student Student { get; set; } public Teacher Teacher { get; set; } public long TeacherId { get; set; } } public class Student : Root { } public class Teacher : Root { } 

现在,在EF中进行此修复后,我可以构建左连接查询,如下所示:

 ctx.Schools .GroupJoin(ctx.Teachers, school => school.TeacherId, teacher => teacher.Id, (school, teachers) => new { school, teachers }) .SelectMany(info => info.teachers.DefaultIfEmpty(), (info, teacher) => new { info.school, teacher }) .Where(info => info.school.Id == someSchoolId) .Select(r => r.school); 

或者像这样:

 from school in ctx.Schools join teacher in ctx.Teachers on school.TeacherId equals teacher.Id into grouping from t in grouping.DefaultIfEmpty() where school.Id == someSchoolId select school; 

生成的sql是:

 SELECT [school].[Id], [school].[StudentId], [school].[TeacherId], [teacher].[Id] FROM [Schools] AS [school] LEFT JOIN [Teachers] AS [teacher] ON [school].[TeacherId] = [teacher].[Id] WHERE [school].[Id] = @__someSchoolId_0 ORDER BY [school].[TeacherId] 

但是(!),当我尝试向左连接添加一个表时

 ctx.Schools .GroupJoin(ctx.Teachers, school => school.TeacherId, teacher => teacher.Id, (school, teachers) => new { school, teachers }) .SelectMany(info => info.teachers.DefaultIfEmpty(), (info, teacher) => new { info.school, teacher }) .GroupJoin(ctx.Students, info => info.school.StudentId, student => student.Id, (info, students) => new {info.school, info.teacher, students}) .SelectMany(info => info.students.DefaultIfEmpty(), (info, student) => new {info.school, info.teacher, student}) .Where(data => data.school.Id == someSchoolId) .Select(r => r.school); 

要么

 from school in ctx.Schools join teacher in ctx.Teachers on school.TeacherId equals teacher.Id into grouping from t in grouping.DefaultIfEmpty() join student in ctx.Students on school.StudentId equals student.Id into grouping2 from s in grouping2.DefaultIfEmpty() where school.Id == someSchoolId select school; 

生成了两个单独的SQL查询:

 SELECT [student].[Id] FROM [Students] AS [student] SELECT [school].[Id], [school].[StudentId], [school].[TeacherId], [teacher].[Id] FROM [Schools] AS [school] LEFT JOIN [Teachers] AS [teacher] ON [school].[TeacherId] = [teacher].[Id] WHERE [school].[Id] = @__someSchoolId_0 ORDER BY [school].[TeacherId] 

看起来有客户端左连接出现。

我究竟做错了什么?

您需要从所有 3个表中进行选择 ,以便当Entity Framework从Linq AST转换为SQL时, 左连接是有意义

 select new { school, t, s }; 

代替

 select school; 

然后,如果您在程序执行期间从Visual Studio检入Debug并将查询的值复制到剪贴板,您将找到 – 如预期的那样 – 在FROM之后的2个LEFT OUTER JOIN

勘误表

从EF 6可以看到2个左外连接。

EF Core记录器写入查询…

无法翻译,将在本地进行评估。

这里唯一的注意事项是 – 在没有选择其他表的情况下 – 首先没有理由找到多个左连接

EF核心设计

基于github repo中看到的unit testing并试图更接近满足OP要求,我建议以下查询

 var querySO = ctx.Schools .Include(x => x.Student) .Include(x => x.Teacher) ; var results = querySO.ToArray(); 

这次我从EF Core Logger看到了几个LEFT JOIN

PRAGMA foreign_keys = ON;执行DbCommand(0ms)[Parameters = [],CommandType =’Text’,CommandTimeout = ’30’]

选择“x”。“SchoolId”,“x”。“StudentId”,“x”。“TeacherId”,“s”。“StudentId”,“s”。“name”,“t”。“TeacherId”,“ T”,‘名’

来自“学校”AS“x”

LEFT JOIN “Students”AS“s”ON“x”。“StudentId”=“s”。“StudentId”

LEFT JOIN “Teachers”AS“t”ON“x”。“TeacherId”=“t”。“TeacherId”

定义了一个模型

 protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity().HasKey(p => p.SchoolId); modelBuilder.Entity().HasKey(p => p.TeacherId); modelBuilder.Entity().HasKey(p => p.StudentId); modelBuilder.Entity().HasOne(s => s.Student) .WithOne().HasForeignKey(s => s.StudentId); modelBuilder.Entity().HasOne(s => s.Teacher) .WithOne().HasForeignKey(s => s.TeacherId); } 

和课程

 public class School { public long SchoolId { get; set; } public long? StudentId { get; set; } public Student Student { get; set; } public Teacher Teacher { get; set; } public long? TeacherId { get; set; } } public class Student { public long StudentId { get; set; } public string name { get; set; } } public class Teacher { public long TeacherId { get; set; } public string name { get; set; } }