Reputation: 1579
I've three tables.
Table A
id name des table2 table3
1 xyz TableA_des1 null 1
2 abc TableA_des2 1 2
3 hgd TableA_des2 2 3
Table B
id name des Active
1 xyz TableB_des1 1
2 abc TableB_des2 1
3 hgd TableB_des2 1
Table C
id name des Active
1 xyz TableC_des1 1
2 abc TableC_des2 1
3 hgd TableC_des2 1
LINQ Query
var res = (from a in TableA
where id = 1
join b in TableB on a.table2 equals b.id into ab
from bdata in ab.DefaultIfEmpty()
where bdata.Active = true
join c in TableC on a.table3 equals c.id into ac
from cdata in ac.DefaultIfEmpty()
where cdata.Active = true
select new { data1 = a.name, data2 = bdata?? string.Empty, data3 = cdata?? string.Empty})
The about query is giving null. On debugging variable res
has null.
Upvotes: 1
Views: 3476
Reputation: 205629
You should avoid putting where
conditions on range variables coming from the right side of a left outer join
, because doing so effectively turns them into inner join
.
Instead, you should either apply the right side filtering before the join:
from a in TableA
where id = 1
join b in TableB.Where(x => a.Active)
on a.table2 equals b.id
into ab
from bdata in ab.DefaultIfEmpty()
join c in TableC.Where(x => x.Active)
on a.table3 equals c.id
into ac
from cdata in ac.DefaultIfEmpty()
...
or include them in the join (when possible):
from a in TableA
where id = 1
join b in TableB
on new { id = a.table2, Active = true } equals new { b.id, b.Active }
into ab
from bdata in ab.DefaultIfEmpty()
join c in TableC
on new { id = a.table3, Active = true } equals new { c.id, c.Active }
into ac
from cdata in ac.DefaultIfEmpty()
...
In order to understand why is that, try to evaluate where bdata.Active == true
when bdata
is null
(i.e. there is no matching record). Actually if this was LINQ to Objects, the above criteria will generate NullReferenceException
. But LINQ to Entities can handle that w/o exceptions, since databases naturally support null
values in queries for a columns which are normally non nullable. So the above simple evaluates to false
, hence is filtering the resulting record and effectively removing the effect of a left outer join
which by definition should return the left side record regardless of whether a matching right side record exists.
Which means that actually there is a third way (althought the first two options are preferable) - include an explicit null
checks:
from a in TableA
where id = 1
join b in TableB
on a.table2 equals b.id
into ab
from bdata in ab.DefaultIfEmpty()
where bdata == null || bdata.Active
join c in TableC
on a.table3 equals c.id
into ac
from cdata in ac.DefaultIfEmpty()
where cdata == null || cdata.Active
...
Upvotes: 4