views:

235

answers:

3
+3  Q: 

Left join in Linq?

There are a lot of questions on SO already about Left joins in Linq, and the ones I've looked at all use the join keyword to achieve the desired end.

This does not make sense to me. Let's say I have the tables Customer and Invoice, linked by a foreign key CustomerID on Invoice. Now I want to run a report containing customer info, plus any invoices. SQL would be:

select c.*, i.*
  from Customer c
  left join Invoice i on c.ID = i.CustomerID

From what I've seen of the answers on SO, people are mostly suggesting:

var q = from c in Customers
        join i in Invoices.DefaultIfEmpty() on c.ID equals i.CustomerID 
        select new { c, i };

I really don't understand how this can be the only way. The relationship between Customer and Invoice is already defined by the LinqToSQL classes; why should I have to repeat it for the join clause? If I wanted an inner join it would simply have been:

var q = from c in Customers
        from i in c.Invoices
        select new { c, i };

without specifying the joined fields!

I tried:

var q = from c in Customers
        from i in c.Invoices.DefaultIfEmpty()
        select new { c, i };

but that just gave me the same result as if it were an inner join.

Is there not a better way of doing this?

A: 

You may probably want to use the 'into' keyword.

Example

emvy
Thanks, but this doesn't really answer my question... is there not a better way, taking advantage of the fact that the relationship is already defined?
Shaul
+2  A: 

While the relationship is already defined (both in the database and in the .dbml markup) the runtime cannot automatically determine if it should use that relationship.

What if there are two relationships in the object model (Person has Parents and Children, both relationships to other Person instances). While cases could be special cased, this would make the system more complex (so more bugs). Remember in SQL you would repeat the specification of the relationship.

Remember indexes and keys are an implementation detail and not part of the relational algebra that underlies the relation model.

If you want a LEFT OUTER JOIN then you need to use "into":

from c in Customers
join i in Invoices on i.CustomerId equals c.CustomerId into inv
...

and inv will have type IEnumerable<Invoivce>, possibly with no instances.

Richard
+1 Good answer, but you're still missing my point. The Invoice specifically has a "Customers" relationship defined, and I want to make use of THAT relationship: "c.Invoices", not just "Invoices". Is there any way to do that?
Shaul
@Shaul: Then do so, just use c.Invoices in the expression. If you want a different identifier then use a let clause.
Richard
+1  A: 

What are you talking about? That from i in c.Invoice.DefaultIfEmpty() is exactly a left join.

        List<string> strings = new List<string>() { "Foo", "" };

        var q = from s in strings
                from c in s.DefaultIfEmpty()
                select new { s, c };

        foreach (var x in q)
        {
            Console.WriteLine("ValueOfStringIs|{0}| ValueOfCharIs|{1}|",
                x.s,
                (int)x.c);
        }

This test produces:

ValueOfStringIs|Foo| ValueOfCharIs|70|
ValueOfStringIs|Foo| ValueOfCharIs|111|
ValueOfStringIs|Foo| ValueOfCharIs|111|
ValueOfStringIs|| ValueOfCharIs|0|
David B
Well blow me down - you are right! Now, why did I think this wasn't working...? OK, I humbly back down... this question resulted from my own misunderstanding. THANKS! :)
Shaul