我们有一个名为的表格Student。该表有一个名为的字段Homeroom,其中的值是该学生所在房间的房间号。该值可以为空。
Student
Homeroom
我们还有第二张桌子叫Staff。该表还具有一个字段,Homeroom用于指示教师分配给哪个教室。该值可以为空。
Staff
但是,当学生的Homeroomnull为空时,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中还有另一种方法可以防止在加入字段时对字段进行空检查吗?
join homeroomTeacher in repo.GetStaff().Where(staff => staff.Homeroom != null)
如果将联接移到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