Linq to Entitiesjoinvs groupjoin
我有networkingsearch,但我仍然无法find一个简单的答案。 有人可以解释一下(用简单的英语)一个GroupJoin
是什么? 与普通的内部Join
有什么不同? 常用吗? 仅仅是方法的语法? 怎么样的查询语法? 一个C#代码的例子会很好。
行为
假设你有两个列表:
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
生成第一个列表中的条目列表,每个列表中都有第二个列表中的一组连接条目。
这就是为什么Join
与SQL中的INNER JOIN
等效的原因: C
中没有条目。 虽然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
的匿名types。 这个查询语法使用引擎盖下的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是一个匿名types,由一个Parent
types的属性和一个IEnumerable<Child>
types的属性组成。 这个查询语法在引擎盖下使用了GroupJoin
方法。
我们可以在后面的查询中select g
,它将select一个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 }
…产生一个有孩子的家长的名单。 这可以通过两个小的添加变成一个父子对的平坦列表:
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)
请注意, 范围variables c
在上面的语句中被重用。 这样做,通过将into g from c in g.DefaultIfEmpty()
中的into g from c in g.DefaultIfEmpty()
的相当于into g from c in g.DefaultIfEmpty()
的值添加到现有的into g from c in g.DefaultIfEmpty()
语句,可以简单地将任何into g from c in g.DefaultIfEmpty()
语句转换为outer join
into g from c in g.DefaultIfEmpty()
。
这是查询(或综合)语法发光的地方。 方法(或stream利)语法显示真正发生的事情,但是很难写:
parents.GroupJoin(children, p => p.Id, c => c.Id, (p, c) => new { p, c }) .SelectMany(x => xcDefaultIfEmpty(), (x,c) => new { xpValue, c?.ChildValue } )
所以在LINQ中一个平坦的outer join
是一个GroupJoin
,由SelectMany
压平。
2.维护秩序
假设父母的名单有点长。 一些用户界面以固定的顺序产生一个选定父母的列表作为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。 在那里,总的想法是,我们通过“外部”input序列来查看,find来自“内部”序列(基于每个序列上的关键投影)的所有匹配项目,然后产生匹配元素对。 GroupJoin是类似的,除了不是产生成对的元素,而是根据该项目和匹配的“内部”项目的顺序产生每个“外部”项目的单个结果 。
唯一的区别是在回报声明:
join :
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); } }
小组join :
var lookup = inner.ToLookup(innerKeySelector, comparer); foreach (var outerElement in outer) { var key = outerKeySelector(outerElement); yield return resultSelector(outerElement, lookup[key]); }
在这里阅读更多:
-
重新实现LINQ到对象:第19部分 – join
-
重新实现LINQ to Objects:Part 22 – GroupJoin