防止LINQ到实体联接中的NULL检查

时间:2013-10-04 13:22:21

标签: sql entity-framework linq-to-entities ef-code-first outer-join

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

我们有一个名为Staff的第二个表。该表还有一个名为Homeroom的字段,用于指示教师分配到哪个教室。该值可以为null。

但是当学生的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

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

我们正在使用Entity Framework和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中还有另一种方法可以防止在加入字段时对字段进行空检查吗? / p>

2 个答案:

答案 0 :(得分:9)

如果将连接移动到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;

fyi,引入了UseDatabaseNullSemantics来修复这种行为,但看起来它们忘记了JOIN语义并且只将它应用于WHERE语义。

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

  

这实质上意味着与以前的版本相比,EF 6现在有些结果集不同。在我看来,这是一个很大的交易!!!!因为它将错误引入我的工作解决方案!!!!

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

答案 1 :(得分:0)

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

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都为空,则联接中的比较将为"A" == "B",这将返回false。