web-dev-qa-db-ja.com

Linq to Entitiesの参加とグループ参加

ウェブで検索しましたが、まだ簡単な答えが見つかりません。 GroupJoinが何であるかを(簡単な英語で)誰か説明してもらえますか?通常の内部Joinとどう違うのですか?一般的に使用されていますか?メソッド構文専用ですか?クエリ構文はどうですか? C#のコード例はNiceです。

152
duyn9uyen

動作

次の2つのリストがあるとします。

Id  Value
1   A
2   B
3   C

Id  ChildValue
1   a1
1   a2
1   a3
2   b1
2   b2

Joinフィールドの2つのリストをIdとすると、結果は次のようになります。

Value ChildValue
A     a1
A     a2
A     a3
B     b1
B     b2

GroupJoinフィールドの2つのリストをIdとすると、結果は次のようになります。

Value  ChildValues
A      [a1, a2, a3]
B      [b1, b2]
C      []

したがって、Joinは、親と子の値のフラットな(表形式の)結果を生成します。
GroupJoinは、最初のリストにエントリのリストを作成し、それぞれが2番目のリストに結合されたエントリのグループを持ちます。

そのため、JoinはSQLのINNER JOINと同等です。Cのエントリはありません。 GroupJoinOUTER JOINと同等です:Cは結果セットにありますが、関連エントリの空のリストがあります(SQL結果セットには行C - nullがあります)。

構文

2つのリストをそれぞれIEnumerable<Parent>IEnumerable<Child>にします。 (Linq to Entitiesの場合:IQueryable<T>)。

Join構文は

from p in Parent
join c in Child on p.Id equals c.Id
select new { p.Value, c.ChildValue }

IEnumerable<X>を返します。Xは、2つのプロパティValueChildValueを持つ匿名型です。このクエリ構文は、内部で Join メソッドを使用します。

GroupJoin構文は

from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }

IEnumerable<Y>を返します。Yは、タイプParentの1つのプロパティとタイプIEnumerable<Child>のプロパティで構成される匿名タイプです。このクエリ構文は、内部で GroupJoin メソッドを使用します。

後者のクエリでselect gを実行するだけで、リストのリストなど、IEnumerable<IEnumerable<Child>>を選択できます。多くの場合、親が含まれるselectの方が便利です。

いくつかのユースケース

1.平らな外部結合を作成します。

述べたように、声明...

from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }

...子グループを持つ親のリストを作成します。これは、2つの小さな追加により、親子ペアのフラットリストに変換できます。

from p in parents
join c in children on p.Id equals c.Id into g // <= into
from c in g.DefaultIfEmpty()               // <= flattens the groups
select new { Parent = p.Value, Child = c?.ChildValue }

結果は次のようになります

Value Child
A     a1
A     a2
A     a3
B     b1
B     b2
C     (null)

range variablecは上記のステートメントで再利用されることに注意してください。これを行うと、既存のjoinステートメントにinto g from c in g.DefaultIfEmpty()に相当するものを追加することで、joinステートメントをouter joinに簡単に変換できます。

これは、クエリ(または包括的な)構文が輝く場所です。メソッド(または流fluent)構文は実際に何が起こるかを示しますが、書くのは難しいです:

parents.GroupJoin(children, p => p.Id, c => c.Id, (p, c) => new { p, c })
       .SelectMany(x => x.c.DefaultIfEmpty(), (x,c) => new { x.p.Value, c?.ChildValue } )

したがって、LINQのフラットなouter joinGroupJoinであり、SelectManyによってフラット化されています。

2.順序を維持する

親のリストがもう少し長いとします。一部のUIは、選択された親のリストを固定の順序でId値として生成します。使用しましょう:

var ids = new[] { 3,7,2,4 };

ここで、選択した親は、この正確な順序で親リストからフィルタリングする必要があります。

もしそうなら...

var result = parents.Where(p => ids.Contains(p.Id));

... parentsの順序が結果を決定します。親がIdで順序付けられている場合、結果は親2、3、4、7になります。ただし、joinを使用してリストをフィルタリングすることもできます。そして、idsを最初のリストとして使用することにより、順序が保持されます:

from id in ids
join p in parents on id equals p.Id
select p

結果は、親3、7、2、4です。

319
Gert Arnold

eduLINQ によると:

GroupJoinの機能を理解する最良の方法は、Joinについて考えることです。そこで、全体的なアイデアは、「外部」入力シーケンスを調べ、「各」シーケンスのキー射影に基づいて「内部」シーケンスからすべての一致するアイテムを見つけ、一致する要素のペアを生成することでした。 GroupJoinも同様ですが、要素のペアを生成する代わりに、そのアイテムと一致する「内部」アイテムのシーケンスに基づいて、各「外部」アイテムに対して単一の結果を生成します

唯一の違いはreturnステートメントです。

参加

var lookup = inner.ToLookup(innerKeySelector, comparer); 
foreach (var outerElement in outer) 
{ 
    var key = outerKeySelector(outerElement); 
    foreach (var innerElement in lookup[key]) 
    { 
        yield return resultSelector(outerElement, innerElement); 
    } 
} 

GroupJoin

var lookup = inner.ToLookup(innerKeySelector, comparer); 
foreach (var outerElement in outer) 
{ 
    var key = outerKeySelector(outerElement); 
    yield return resultSelector(outerElement, lookup[key]); 
} 

詳細はこちら:

17
MarcinJuraszek