All these transformations are described in C# specification. 7.16.2 Query expression translation is all about that part of C#.
According to that specification there are two cases for join
with an into
:
A query expression with a join
clause with an into
followed by a
select
clause
from x1 in e1
join x2 in e2 on k1 equals k2 into g
select v
is translated into
( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )
A query expression with a join
clause with an into
followed by
something other than a select
clause
from x1 in e1
join x2 in e2 on k1 equals k2 into g
…
is translated into
from * in ( e1 ) . GroupJoin(
e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g })
…