【问题标题】:LINQ Inner-Join vs Left-JoinLINQ 内连接与左连接
【发布时间】: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();

【问题讨论】:

    标签: c# .net linq left-join


    【解决方案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();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-29
          • 2017-10-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多