Linq to Entities加入vs groupjoin

时间:2013-03-24 04:49:24

标签: c# linq linq-to-entities

我有网络搜索,但我仍然无法找到一个简单的答案。有人可以解释(用简单的英文)GroupJoin是什么?它与常规内部Join有什么不同?它常用吗?它只适用于方法语法吗?查询语法怎么样?一个c#代码示例会很好。

3 个答案:

答案 0 :(得分:316)

行为

假设您有两个列表:

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 JOINC没有条目。虽然GroupJoin相当于OUTER JOINC在结果集中,但是有一个相关条目的空列表(在SQL结果集中会有一行{{1} })。

语法

因此,请将两个列表分别设为C - nullIEnumerable<Parent>。 (如果是Linq to Entities:IEnumerable<Child>)。

IQueryable<T> 语法为

Join

返回from p in Parent join c in Child on p.Id equals c.Id select new { p.Value, c.ChildValue } ,其中X是具有两个属性IEnumerable<X>Value的匿名类型。此查询语法使用了Join方法。

ChildValue 语法为

GroupJoin

返回from p in Parent join c in Child on p.Id equals c.Id into g select new { Parent = p, Children = g } ,其中Y是一个匿名类型,由一个IEnumerable<Y>类型的属性和一个类型为Parent的属性组成。此查询语法使用了GroupJoin方法。

我们可以在后一个查询中执行IEnumerable<Child>,这将选择select g,例如列表列表。在许多情况下,包含父项的select更有用。

一些用例

1。生成扁平外连接。

如上所述,陈述......

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

因此,LINQ中的单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,由GroupJoin展平。

2。保留订单

假设父母列表有点长。某些UI会按固定顺序生成所选父项的列表作为SelectMany值。我们来使用:

Id

现在必须按照这个确切的顺序从父母列表中筛选出选定的父母。

如果我们这样做......

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

... var result = parents.Where(p => ids.Contains(p.Id)); 的顺序将决定结果。如果父母是按parents订购的,结果将是父母2,3,4,7。不好。但是,我们也可以使用Id来过滤列表。通过使用join作为第一个列表,订单将被保留:

ids

结果是父母3,7,2,4。

答案 1 :(得分:17)

根据eduLINQ

  

掌握GroupJoin所做的最好的方法就是想到   加入。在那里,总的想法是我们透过“外部”看   输入序列,找到“内部”序列中的所有匹配项   (基于每个序列上的关键投影)然后产生对   匹配元素。 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]); 
} 

在这里阅读更多内容:

答案 2 :(得分:1)

假设您有两个不同的类:

public class Person
{
    public string Name, Email;
    
    public Person(string name, string email)
    {
        Name = name;
        Email = email;
    }
}
class Data
{
    public string Mail, SlackId;
    
    public Data(string mail, string slackId)
    {
        Mail = mail;
        SlackId = slackId;
    }
}

现在,让我们准备要使用的数据:

var people = new Person[]
    {
        new Person("Sudi", "sudi@try.cd"),
        new Person("Simba", "simba@try.cd"),
        new Person("Sarah", string.Empty)
    };
    
    var records = new Data[]
    {
        new Data("sudi@try.cd", "Sudi_Try"),
        new Data("sudi@try.cd", "Sudi@Test"),
        new Data("simba@try.cd", "SimbaLion")
    };

您会注意到sudi@try.cd有两个slackId。我这样做是为了说明 加入作品。

现在让我们构造查询以将Person与Data连接起来

var query = people.Join(records,
        x => x.Email,
        y => y.Mail,
        (person, record) => new { Name = person.Name, SlackId = record.SlackId});
    Console.WriteLine(query);

构造查询后,您还可以使用如下的foreach对其进行迭代:

foreach (var item in query)
    {
        Console.WriteLine($"{item.Name} has Slack ID {item.SlackId}");
    }

我们还要为GroupJoin输出结果:

Console.WriteLine(
    
        people.GroupJoin(
            records,
            x => x.Email,
            y => y.Mail,
            (person, recs) => new {
                Name = person.Name,
                SlackIds = recs.Select(r => r.SlackId).ToArray() // You could materialize //whatever way you want.
            }
        ));

您会注意到, GroupJoin 会将所有SlackId放在一个组中。