Answers:
假设您有两个列表:
Id Value
1 A
2 B
3 C
Id ChildValue
1 a1
1 a2
1 a3
2 b1
2 b2
当您Join
在该Id
字段上的两个列表时,结果将是:
Value ChildValue
A a1
A a2
A a3
B b1
B b2
当您GroupJoin
在该Id
字段上的两个列表时,结果将是:
Value ChildValues
A [a1, a2, a3]
B [b1, b2]
C []
因此Join
产生父值和子值的平坦(表格)结果。
GroupJoin
在第一个列表中产生一个条目列表,每个条目在第二个列表中都有一组联接的条目。
这就是在SQL中Join
与之等效的原因INNER JOIN
:没有的条目C
。While 与结果集中GroupJoin
的OUTER JOIN
:等效C
,但是相关条目的列表为空(在SQL结果集中将存在一行C - null
)。
因此,让两个列表分别为IEnumerable<Parent>
和IEnumerable<Child>
。(如果是对实体的Linq :)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是具有两个属性Value
和的匿名类型ChildValue
。此查询语法使用幕后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>
where Y是一个匿名类型,该匿名类型由type的一个属性和type的Parent
一个属性组成IEnumerable<Child>
。此查询语法使用幕后GroupJoin
方法。
我们可以select g
在后面的查询中进行选择,即选择一个IEnumerable<IEnumerable<Child>>
,例如一个列表列表。在许多情况下,包含父项的选择更为有用。
如前所述,该声明...
from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }
...产生一个有孩子群体的父母名单。可以通过两个小的添加将其变成平面的父子对列表:
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)
请注意,上面的语句中重新使用了范围变量 c
。这样做,join
只要将outer join
等价物添加into g from c in g.DefaultIfEmpty()
到现有join
语句中,就可以简单地将其转换为。
这是查询(或综合)语法的亮点。方法(或流利的)语法显示了实际发生的情况,但是很难编写:
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 } )
因此outer join
LINQ中的单位是GroupJoin
,由展平SelectMany
。
假设父母的名单更长一些。某些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。
根据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);
}
}
群组加入:
var lookup = inner.ToLookup(innerKeySelector, comparer);
foreach (var outerElement in outer)
{
var key = outerKeySelector(outerElement);
yield return resultSelector(outerElement, lookup[key]);
}
在这里阅读更多: