と思ったら、where句を使うことで複数のテーブルと外部結合ができるようだ。ということでテーブル構成は以下の図にある3テーブルを作り、dtMainにdtSub1,dtSub2を外結するイメージで作ってみる。

(1) 「Microsoft Visual Studio Express 2012 for Windows Desktop」を開き、新規で「Windowsフォームアプリケーション」を開く
(2) Form1に適当にDataGridViewをいれておく。

(3) Form1.vbに以下のコードを追記
Public Class Form1
Public Sub New()
' この呼び出しはデザイナーで必要です。
InitializeComponent()
' InitializeComponent() 呼び出しの後で初期化を追加します。
Construct()
End Sub
Private Sub Construct()
' テーブル定義
Dim dtMain As New DataTable
dtMain.Columns.Add(New DataColumn With {.DataType = System.Type.GetType("System.Int32"), .ColumnName = "ID"})
dtMain.Columns.Add(New DataColumn With {.DataType = System.Type.GetType("System.String"), .ColumnName = "NAME"})
Dim dtSub1 As DataTable = dtMain.Clone
Dim dtSub2 As DataTable = dtMain.Clone
Dim dtKekka As DataTable = dtMain.Clone
Dim dtRow As DataRow = Nothing
' データインサート
For i As Integer = 1 To 3
dtRow = dtMain.NewRow
dtRow("ID") = i
dtMain.Rows.Add(dtRow)
If i = 1 Then
dtRow("NAME") = "外結DT1"
dtSub1.ImportRow(dtRow)
ElseIf i = 3 Then
dtRow("NAME") = "外結DT2"
dtSub2.ImportRow(dtRow)
End If
Next
' 外結クエリ①
Dim query = From main In dtMain.AsEnumerable _
From sub1 In dtSub1.AsEnumerable.Where(Function(m) m.Field(Of Integer)("ID") = main.Field(Of Integer)("ID")).DefaultIfEmpty() _
From sub2 In dtSub2.AsEnumerable.Where(Function(m) m.Field(Of Integer)("ID") = main.Field(Of Integer)("ID")).DefaultIfEmpty() _
Select New With _
{ _
.id = main!ID, _
.name = If(sub1 Is Nothing, If(sub2 Is Nothing, main!NAME, sub2!NAME), sub1!NAME) _
}
For Each row In query
dtRow = dtKekka.NewRow
dtRow("ID") = row.id
dtRow("NAME") = row.name
dtKekka.Rows.Add(dtRow)
Next
Me.DataGridView1.DataSource = dtKekka
End Sub
End Class
(4) ソリューションをビルド
①の外結クエリのところで実際に外部結合。
dtMainのIDに対し、dtSub1とdtSub2のIDを外結。Select句ではIDはdtMainのものを表示。NAMEはdtSub1に有効なNameがあればそれを表示、なければdtSub2に有効なNameがあればそれを表示、それ以外はdtMainのName(NULL)を表示。という感じ。
ということで実行。

想定通り、IDは全行表示、NAMEはあれば表示された。
Group JoinだとInto句でリスト化を挟むとかイマイチ不明な過程があったが、Where句だとそういう考慮も無くすっきり書けてイメージがとらえやすい感じ。
sub1やsub2でゴリゴリと長ったらしい集計クエリーを書いてももちろん外結してくれるので良い感じ。このサンプルはわざわざDataTableを挟んでるけど、もちろん普通のエンティティデータのクエリでもいけました。
しかしDataTableはAsEnumerable化してもデータ数が多いと集計が遅いね…。一回DataTableのデータをEntityの配列に入れなおして集計すると凄い早い。というか最初からDataTableを使うなよってことですが、何か使ってしまう。