Prevent NULL checks in LINQ to Entity Joins

北城余情 提交于 2020-01-13 11:33:06

问题


We have a table called Student. That table has a field called Homeroom, where the value is a room number of the student's homeroom. The value can be null.

We have a second table called Staff. That table also has a field called Homeroom to indicate which homeroom the teacher is assigned to. The value can be null.

But when the student's Homeroom is null, a Staff record should not be returned.

We used to take advantage of the fact that checking two null fields for equality always returns false in SQL. Through SQL, this is how we would get the data we want:

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

Student would be returned, but no teacher.

We are using Entity Framework with Code First POCO objects. So, we have a Student object and a Staff object. When we recreate this SQL in LINQ:

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;

The SQL generated contains a NULL check on both Homeroom fields:

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))
    )

This will return the student, and any staff who does not have a homeroom defined. That's not what we wanted or expected based on how we previously wrote our SQL statements.

An obvious way around it is to make sure we don't include staff that do not have a homeroom (join homeroomTeacher in repo.GetStaff().Where(staff => staff.Homeroom != null). But is there another way in the LINQ to prevent null checks on fields when joining them?


回答1:


if you move your join into a where clause, then the following setting on the DbContext object will turn off the (EF 6 introduced) NULL checking behaviour:

Context.Configuration.UseDatabaseNullSemantics = true;

So, to "Join" in a where clause, you can split out the query into 2 IQueryable objects

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, the UseDatabaseNullSemantics was introduced to fix-up this behaviour but it looks like they forgot the JOIN semantics and only applied it to the WHERE semantics.

This original statement was wrong - EF 4.3.1 exhibited the same JOIN behaviour:

This essentially means some result sets are now different for EF 6, compared to previous versions. This, in my mind, IS A BIG DEAL !!!! as it introduced bugs into my working solutions !!!!

I have raised an issue on codeplex:https://entityframework.codeplex.com/workitem/2006




回答2:


I don't have a data model handy to test this, but it should be possible to simulate the null != null behavior of SQL by coalescing the homeroom values to different strings (or ints, or whatever the type is):

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;

This way if student.Homeroom and homeroomTeacher.Homeroom are both null, the comparison in the join will be "A" == "B", which will return false.



来源:https://stackoverflow.com/questions/19182319/prevent-null-checks-in-linq-to-entity-joins

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!