我们有一个名为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>
答案 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.Homeroom
和homeroomTeacher.Homeroom
都为空,则联接中的比较将为"A" == "B"
,这将返回false。