4

我们有一个名为 的表Student。该表有一个名为 的字段Homeroom,其中的值是学生主室的房间号。该值可以为空。

我们有第二张桌子叫做Staff. 该表还有一个字段Homeroom,用于指示教师被分配到哪个教室。该值可以为空。

但是当学生的Homeroom为空时,Staff不应返回记录。

我们曾经利用这样一个事实,即检查两个空字段是否相等在 SQL 中总是返回 false。通过 SQL,我们可以这样获取我们想要的数据:

SELECT STUDENT.ID, STAFF.NAME as [Homeroom Teacher]
FROM STUDENT
LEFT OUTER JOIN STAFF ON
    STAFF.BUILDING = STUDENT.BUILDING AND
    STAFF.HOMEROOM = STUDENT.HOMEROOM

学生将被退回,但没有老师。

我们正在使用带有 Code First POCO 对象的实体框架。所以,我们有一个 Student 对象和一个 Staff 对象。当我们在 LINQ 中重新创建此 SQL 时:

from student in repo.GetStudents()
join homeroomTeacher in repo.GetStaff()
    new { student.Building, Room = student.Homeroom }
     equals new { homeroomTeacher.Building, Room = homeroomTeacher.Homeroom }
into roj2
from homeroomTeacherRoj in roj2.DefaultIfEmpty()
select student.Id, homeroomTeacherRoj.Name;

生成的 SQL 包含对两个 Homeroom 字段的 NULL 检查:

SELECT STUDENT.ID, STAFF.NAME
FROM STUDENT AS [Extent1]
LEFT OUTER JOIN [dbo].[STAFF] AS [Extent2] ON 
    ([Extent1].[BUILDING] = [Extent2].[BUILDING]) AND 
    (
        ([Extent1].[HOMEROOM] = [Extent2].[HOMEROOM]) OR 
        (([Extent1].[HOMEROOM] IS NULL) AND ([Extent2].[HOMEROOM] IS NULL))
    )

这将返回学生,以及没有定义班主任的任何教职员工。根据我们之前编写 SQL 语句的方式,这不是我们想要或期望的。

一个明显的解决方法是确保我们不包括没有教室的员工(join homeroomTeacher in repo.GetStaff().Where(staff => staff.Homeroom != null)。但是在 LINQ 中是否有另一种方法可以防止在加入他们时对字段进行空检查?

4

2 回答 2

11

如果您将联接移动到 where 子句中,则 DbContext 对象上的以下设置将关闭(引入 EF 6)NULL 检查行为:

Context.Configuration.UseDatabaseNullSemantics = true;

因此,要在 where 子句中“加入”,您可以将查询拆分为 2 个 IQueryable 对象

var subquery = from homeroomTeacher in repo.GetStaff()
               where ...
               select homeroomTeacher;

var query = from student in repo.GetStudents()
            where subquery.Any(homeroomTeacher => 
                 homeroomTeacher.xxx == student.xxx) -- simplified join for demo code
            select student;

仅供参考,引入了 UseDatabaseNullSemantics 来修复此行为,但看起来他们忘记了 JOIN 语义,只将其应用于 WHERE 语义。

这个原始陈述是错误的 - EF 4.3.1 表现出相同的 JOIN 行为:

这实质上意味着 EF 6 的某些结果集现在与以前的版本不同。在我看来,这很重要!!!!因为它在我的工作解决方案中引入了错误!!!!

我在 codeplex 上提出了一个问题:https ://entityframework.codeplex.com/workitem/2006

于 2014-01-22T00:29:20.123 回答
0

我没有方便的数据模型来测试它,但是应该可以null != null通过将 homeroom 值合并到不同的字符串(或整数,或任何类型)来模拟 SQL 的行为:

from student in repo.GetStudents()
join homeroomTeacher in repo.GetStaff() 
    on new { 
        student.Building, 
        Room = student.Homeroom ?? "A"
    }
    equals new { 
        homeroomTeacher.Building, 
        Room = homeroomTeacher.Homeroom ?? "B"
    }
into roj2
from homeroomTeacherRoj in roj2.DefaultIfEmpty()
select student.Id, homeroomTeacherRoj.Name;

这样,如果student.HomeroomhomeroomTeacher.Homeroom都为 null,则连接中的比较将为"A" == "B",这将返回 false。

于 2013-10-05T19:15:22.820 回答