I have written a Linq query to do an outer join on multiple columns. My problem is that I want to OR the columns. In other words, I need the join condition:
on Bar.Id equals Foo.BarId1 or Bar.Id equals Foo.BarId2
Some examples I have seen on this site have used anonymous types to create AND conditions, but I can't figure out how to construct an OR using anonymous type or if it is even possible. I can find no example that matches.
My query:
var data = (from f in Foos
join b in Bars on f.BarId1 equals b.Id into tb
from xb in tb.DefaultIfEmpty()
join b in Bars on f.BarId2 equals b.Id into tb2
from xb2 in tb2.DefaultIfEmpty()
select new { Foo = f, Bar1 = xb, Bar2 = xb2 });
This works, but I have a feeling it isn't the best solution
EDIT
I spoke too soon. It doesn't work: when Foo.BarId1 points to a valid Bar, and Foo.BarId2 does not, it actually returns the same Bar for Bar1 and Bar2. I was expecting Bar1 to be valid and Bar2 to be null in this case.
EDIT
I'm getting closer. This is my latest query:
var data = from f in Foos
from b in Bars.Where(x => x.Id == p.BarId1 || x.Id == p.BarId2).DefaultIfEmpty()
select new { Foo = p, Bars = b };
I was expecting it to return:
Foo Bar
f1 b1
b2
What I get is:
Foo Bar
f1 b1
f1 b2
EDIT
I've finally found the query that returns exactly what I want:
var data = from f in Foos
select new
{
Foo = f,
Bar1 = Bars.FirstOrDefault(x => x.Id == f.Bar1Id),
Bar2 = Bars.FirstOrDefault(x => x.Id == f.Bar2Id)
};
Would still like to know of any improvements I can make to this.
FINAL EDIT
I have gone back to the original query:
var data = (from f in Foos
join b in Bars on f.BarId1 equals b.Id into tb
from xb in tb.DefaultIfEmpty()
join b in Bars on f.BarId2 equals b.Id into tb2
from xb2 in tb2.DefaultIfEmpty()
select new { Foo = f, Bar1 = xb, Bar2 = xb2 });
It turns out that this was in fact working, but an error in my Linqpad test made me think it wasn't.
It is more efficient than the previous one too - in SQL Profiler I can see that it generates 1 SQL select, compared to the previous, which generates 3*n selects (does 2 Bar selects for every Foo select)