LINQ 内连接与左连接

Posted

技术标签:

【中文标题】LINQ 内连接与左连接【英文标题】:LINQ Inner-Join vs Left-Join 【发布时间】:2010-10-06 05:28:17 【问题描述】:

使用扩展语法,我尝试使用 LINQ 在我拥有的两个列表上创建左连接。以下来自 Microsoft 帮助,但我已对其进行了修改以显示宠物列表没有任何元素。我最终得到的是一个包含 0 个元素的列表。我认为这是因为内部连接正在发生。我想要结束的是一个包含 3 个元素(3 个 Person 对象)的列表,其中为缺失的元素填充了空数据。即左连接。这可能吗?

Person magnus = new Person  Name = "Hedlund, Magnus" ;
Person terry = new Person  Name = "Adams, Terry" ;
Person charlotte = new Person  Name = "Weiss, Charlotte" ;

//Pet barley = new Pet  Name = "Barley", Owner = terry ;
//Pet boots = new Pet  Name = "Boots", Owner = terry ;
//Pet whiskers = new Pet  Name = "Whiskers", Owner = charlotte ;
//Pet daisy = new Pet  Name = "Daisy", Owner = magnus ;

List<Person> people = new List<Person>  magnus, terry, charlotte ;
//List<Pet> pets = new List<Pet>  barley, boots, whiskers, daisy ;
List<Pet> pets = new List<Pet>();

// Create a list of Person-Pet pairs where 
// each element is an anonymous type that contains a
// Pet's name and the name of the Person that owns the Pet.
var query =
    people.Join(pets,
                person => person,
                pet => pet.Owner,
                (person, pet) =>
                    new  OwnerName = person.Name, Pet = pet.Name ).ToList();

【问题讨论】:

【参考方案1】:

我认为如果你想使用扩展方法你需要使用GroupJoin

var query =
    people.GroupJoin(pets,
                     person => person,
                     pet => pet.Owner,
                     (person, petCollection) =>
                        new  OwnerName = person.Name,
                              Pet = PetCollection.Select( p => p.Name )
                                                 .DefaultIfEmpty() 
                    ).ToList();

您可能不得不使用选择表达式。在一对多关系的情况下,我不确定它是否会满足您的需求。

我认为使用 LINQ 查询语法会更容易一些

var query = (from person in context.People
             join pet in context.Pets on person equals pet.Owner
             into tempPets
             from pets in tempPets.DefaultIfEmpty()
             select new  OwnerName = person.Name, Pet = pets.Name )
            .ToList();

【讨论】:

哇 :) 正是我想要的! 我不知道您可以通过在查询方法周围加上括号来获得 .ToList(),谢谢! 请不要将 LINQ 声明性查询语法称为“LINQ 语法”。它们都是“LINQ 语法”。正确的命名是“查询语法”与“方法语法”。 msdn.microsoft.com/en-us/library/bb397947.aspx 后者的不当命名是“yoda-style syntax”。【参考方案2】:

您需要将连接的对象放入一个集合中,然后按照 JPunyon 所说的应用 DefaultIfEmpty:

Person magnus = new Person  Name = "Hedlund, Magnus" ;
Person terry = new Person  Name = "Adams, Terry" ;
Person charlotte = new Person  Name = "Weiss, Charlotte" ;

Pet barley = new Pet  Name = "Barley", Owner = terry ;
List<Person> people = new List<Person>  magnus, terry, charlotte ;
List<Pet> pets = new List<Pet>barley;

var results =
    from person in people
    join pet in pets on person.Name equals pet.Owner.Name into ownedPets
    from ownedPet in ownedPets.DefaultIfEmpty(new Pet())
    orderby person.Name
    select new  OwnerName = person.Name, ownedPet.Name ;


foreach (var item in results)

    Console.WriteLine(
        String.Format("0,-25 has 1", item.OwnerName, item.Name ) );

输出:

Adams, Terry              has Barley
Hedlund, Magnus           has
Weiss, Charlotte          has

【讨论】:

感谢 Gishu - 非常有用的信息。 我认为您可以将两行:join pet...from ownedPet... 替换为单行:from pet in pets.Where(x =&gt; person.Name == x.Owner.Name).DefaultIfEmpty()【参考方案3】:

当遇到同样的问题时,我会收到以下错误消息:

连接子句中的其中一个表达式的类型不正确。调用“GroupJoin”时类型推断失败。

当我使用相同的属性名称时解决了,它起作用了。

(...)

join enderecoST in db.PessoaEnderecos on 
    new 
        
         CD_PESSOA          = nf.CD_PESSOA_ST, 
         CD_ENDERECO_PESSOA = nf.CD_ENDERECO_PESSOA_ST 
       equals 
    new 
     
         enderecoST.CD_PESSOA, 
         enderecoST.CD_ENDERECO_PESSOA 
     into eST

(...)

【讨论】:

为此 +1。我盯着我的代码看了 15 分钟,试图理解为什么 2 个具有相同类型的匿名类不一样。然后我添加了明确的属性名称...【参考方案4】:

这是 Fabrice(LINQ in Action 的作者)刚刚发布的一篇很好的博客文章,其中涵盖了我提出的问题中的材料。我把它放在这里供参考,因为问题的读者会发现这很有用。

Converting LINQ queries from query syntax to method/operator syntax

【讨论】:

【参考方案5】:

使用 DefaultIfEmpty() 方法可以在 LINQ 中进行左连接。不过,我没有适合您的情况的确切语法...

实际上,我认为如果您只是在查询中将 pets 更改为 pets.DefaultIfEmpty() 它可能会起作用...

编辑:我真的不应该在很晚的时候回答问题......

【讨论】:

【参考方案6】:

如果你真的有一个数据库,这是最简单的方法:

var lsPetOwners = ( from person in context.People
                    from pets in context.Pets
                        .Where(mypet => mypet.Owner == person.ID) 
                        .DefaultIfEmpty()
                     select new  OwnerName = person.Name, Pet = pets.Name 
                   ).ToList();

【讨论】:

以上是关于LINQ 内连接与左连接的主要内容,如果未能解决你的问题,请参考以下文章

内连接与左外链接的区别

数据库表连接(内连接,外连接左连接右连接全连接交叉连接)

右连接与左连接

Linq 和 SQL的左连接右连接内链接

LINQ的左连接右连接内连接

LINQ的左连接右连接内连接