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中没有条目。 虽然GroupJoinOUTER 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是具有两个属性ValueChildValue的匿名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,由一个Parenttypes的属性和一个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