Left join using LINQ

前端 未结 5 1637
心在旅途
心在旅途 2021-01-12 05:31

Could someone give me an example of how to perform a left join operation using LINQ/lambda expressions?

5条回答
  •  清歌不尽
    2021-01-12 05:48

    The way I have found that I like is to combine OuterCollection.SelectMany() with InnerCollection.DefaultIfEmpty(). You can run the following in LINQPad using "C# Statements" mode.

    var teams = new[] 
        { 
            new { Id = 1, Name = "Tigers" }, 
            new { Id = 2, Name = "Sharks" }, 
            new { Id = 3, Name = "Rangers" },
        };
    
    var players = new[] 
        { 
            new { Name = "Abe", TeamId = 2}, 
            new { Name = "Beth", TeamId = 4}, 
            new { Name = "Chaz", TeamId = 1}, 
            new { Name = "Dee", TeamId = 2}, 
        };
    
    // SelectMany generally aggregates a collection based upon a selector: from the outer item to
    //  a collection of the inner item.  Adding .DefaultIfEmpty ensures that every outer item
    //  will map to something, even null.  This circumstance makes the query a left outer join.
    // Here we use a form of SelectMany with a second selector parameter that performs an
    //  an additional transformation from the (outer,inner) pair to an arbitrary value (an
    //  an anonymous type in this case.)
    var teamAndPlayer = teams.SelectMany(
        team => 
            players
            .Where(player => player.TeamId == team.Id)
            .DefaultIfEmpty(),
        (team, player) => new 
            { 
                 Team = team.Name, 
                 Player = player != null ? player.Name : null 
            });
    
    teamAndPlayer.Dump();
    
    // teamAndPlayer is:
    //     { 
    //         {"Tigers", "Chaz"},
    //         {"Sharks", "Abe"},
    //         {"Sharks", "Dee"},
    //         {"Rangers", null}
    //     }
    

    While experimenting with this, I found that sometimes you can omit the null-check of player in the instantiation of the anonymous type. I think that this is the case when using LINQ-to-SQL on a database (instead of these arrays here, which I think makes it LINQ-to-objects or something.) I think that the omission of the null check works in LINQ-to-SQL because the query is translated into a SQL LEFT OUTER JOIN, which skips straight to joining null with the outer item. (Note that the value of the anonymous object's property must be nullable; so if you want to safely include an int, say, you would need something like: new { TeamId = (int?)player.TeamId }.

提交回复
热议问题