From cb2c3c2c7b141495c4bb768626492afcbcad168d Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Wed, 23 Sep 2026 14:04:45 +0200 Subject: [PATCH] Outer join in LINQ: retarget net10.0, add LeftJoin and RightJoin examples - Retarget both projects from net8.0 to net10.0. - Bump test packages: Microsoft.NET.Test.Sdk 18.10.1, xunit 2.9.3, xunit.runner.visualstudio 4.0.0, coverlet.collector 10.0.1. - Add PerformLeftJoinWithLeftJoinMethod and PerformRightJoinWithRightJoinMethod using the .NET 10 Enumerable.LeftJoin and Enumerable.RightJoin operators. - Add two tests for the new methods (8 tests become 10). - SongWithAuthorComparer: Equals returns the expression directly, and GetHashCode combines Title and AuthorName with HashCode.Combine. - Program.cs prints the two new joins. --- ...CreateAnOuterJoinInLINQLeftAndRight.csproj | 2 +- .../Program.cs | 26 ++++++++++ .../SongWithAuthorComparer.cs | 9 +--- .../Utilities.cs | 32 ++++++++++++ ...teAnOuterJoinInLINQLeftAndRightUnitTest.cs | 50 +++++++++++++++++++ .../Tests/Tests.csproj | 10 ++-- 6 files changed, 116 insertions(+), 13 deletions(-) diff --git a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight.csproj b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight.csproj index 2150e3797b..ed9781c223 100644 --- a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight.csproj +++ b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net10.0 enable enable diff --git a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/Program.cs b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/Program.cs index 769e5ee466..d6a0e64c16 100644 --- a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/Program.cs +++ b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/Program.cs @@ -52,6 +52,32 @@ Console.WriteLine(); Console.WriteLine(); +Console.WriteLine("Left outer join - LeftJoin method"); +Console.WriteLine(); + +results = Utilities.PerformLeftJoinWithLeftJoinMethod(Utilities.Songs, Utilities.Authors); + +foreach (var item in results) +{ + Console.WriteLine($"Title: {item.Title}, Author: {item.AuthorName}"); +} + +Console.WriteLine(); +Console.WriteLine(); + +Console.WriteLine("Right outer join - RightJoin method"); +Console.WriteLine(); + +results = Utilities.PerformRightJoinWithRightJoinMethod(Utilities.Songs, Utilities.Authors); + +foreach (var item in results) +{ + Console.WriteLine($"Title: {item.Title}, Author: {item.AuthorName}"); +} + +Console.WriteLine(); +Console.WriteLine(); + Console.WriteLine("Full outer join"); Console.WriteLine(); diff --git a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/SongWithAuthorComparer.cs b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/SongWithAuthorComparer.cs index 8ec28261ae..29e0f19d53 100644 --- a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/SongWithAuthorComparer.cs +++ b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/SongWithAuthorComparer.cs @@ -6,16 +6,11 @@ internal class SongWithAuthorComparer : IEqualityComparer { public bool Equals(SongWithAuthor? x, SongWithAuthor? y) { - if (x?.AuthorName == y?.AuthorName && x?.Title == y?.Title) - { - return true; - } - - return false; + return x?.AuthorName == y?.AuthorName && x?.Title == y?.Title; } public int GetHashCode([DisallowNull] SongWithAuthor obj) { - return obj.AuthorName.GetHashCode(); + return HashCode.Combine(obj.Title, obj.AuthorName); } } diff --git a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/Utilities.cs b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/Utilities.cs index 7c27f5961d..76f14fc56a 100644 --- a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/Utilities.cs +++ b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/HowToCreateAnOuterJoinInLINQLeftAndRight/Utilities.cs @@ -92,6 +92,38 @@ public static List PerformRightJoinWithMethodSyntax( return results.ToList(); } + + public static List PerformLeftJoinWithLeftJoinMethod( + List songs, List authors) + { + var results = songs.LeftJoin( + authors, + song => song.AuthorId, + author => author.Id, + (song, author) => new SongWithAuthor + { + Title = song.Title, + AuthorName = author?.Name ?? "unknown" + }); + + return results.ToList(); + } + + public static List PerformRightJoinWithRightJoinMethod( + List songs, List authors) + { + var results = songs.RightJoin( + authors, + song => song.AuthorId, + author => author.Id, + (song, author) => new SongWithAuthor + { + Title = song?.Title ?? "-", + AuthorName = author.Name + }); + + return results.ToList(); + } public static List PerformFullOuterJoin( List songs, List authors) diff --git a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/Tests/HowToCreateAnOuterJoinInLINQLeftAndRightUnitTest.cs b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/Tests/HowToCreateAnOuterJoinInLINQLeftAndRightUnitTest.cs index 7a5c713c13..ffbcf6f51c 100644 --- a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/Tests/HowToCreateAnOuterJoinInLINQLeftAndRightUnitTest.cs +++ b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/Tests/HowToCreateAnOuterJoinInLINQLeftAndRightUnitTest.cs @@ -104,6 +104,56 @@ public void GivenTwoDataSources_WhenPerformRightJoinWithMethodSyntaxMethodCalled Assert.Equal(authors.Count, output); } + [Fact] + public void GivenTwoDataSources_WhenPerformLeftJoinWithLeftJoinMethodCalled_ThenAllItemsFromLeftSourceAreReturned() + { + // Arrange + List songs = + [ + new() { Id = 1, Title = "song A", AuthorId = 3 }, + new() { Id = 2, Title = "song B", AuthorId = 1 }, + new() { Id = 3, Title = "song C", AuthorId = 5 }, + new() { Id = 4, Title = "song D", AuthorId = 2 } + ]; + + List authors = + [ + new() { Id = 1, Name = "Author A" }, + new() { Id = 9, Name = "Author B" } + ]; + + // Act + var output = Utilities.PerformLeftJoinWithLeftJoinMethod(songs, authors).Count; + + // Assert + Assert.Equal(songs.Count, output); + } + + [Fact] + public void GivenTwoDataSources_WhenPerformRightJoinWithRightJoinMethodCalled_ThenAllItemsFromRightSourceAreReturned() + { + // Arrange + List songs = + [ + new() { Id = 1, Title = "song A", AuthorId = 3 }, + new() { Id = 2, Title = "song B", AuthorId = 1 }, + new() { Id = 3, Title = "song C", AuthorId = 5 }, + new() { Id = 4, Title = "song D", AuthorId = 2 } + ]; + + List authors = + [ + new() { Id = 1, Name = "Author A" }, + new() { Id = 9, Name = "Author B" } + ]; + + // Act + var output = Utilities.PerformRightJoinWithRightJoinMethod(songs, authors).Count; + + // Assert + Assert.Equal(authors.Count, output); + } + [Fact] public void GivenTwoDataSources_WhenPerformFullOuterJoinMethodCalled_ThenAllItemsFromBothSourcesAreReturned() { diff --git a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/Tests/Tests.csproj b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/Tests/Tests.csproj index 615d2e1e84..b79bfbeb8f 100644 --- a/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/Tests/Tests.csproj +++ b/csharp-linq/HowToCreateAnOuterJoinInLINQLeftAndRight/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable @@ -10,13 +10,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all