New in .NET 10.0 [23]: New LINQ operators LeftJoin() and RightJoin()

A Left (Outer) Join and a Right (Outer) Join were already possible in LINQ. In .NET 10.0, there are now dedicated operators for this.

listen Print view
Traffic sign with inscription .NET

(Image: Pincasso / Shutterstock.com)

1 min. read
By
  • Dr. Holger Schwichtenberg

As with the last .NET versions, Microsoft is again releasing new operators for Language Integrated Query (LINQ) in .NET 10.0 that simplify existing constructs. This time, with LeftJoin() and RightJoin(), there are two elementary operators, one from set theory and the other for relational databases. More on this can be found in the associated issue on GitHub.

The Dotnet Doctor – Holger Schwichtenberg
Der Dotnet-Doktor – Holger Schwichtenberg

Dr. Holger Schwichtenberg is the technical director of the expert network www.IT-Visions.de, which supports numerous medium-sized and large companies with consulting and training services as well as software development, drawing on the expertise of 53 renowned experts. Thanks to his appearances at numerous national and international conferences, as well as more than 90 specialist books and over 1,500 specialist articles, Holger Schwichtenberg is one of the best-known experts for .NET and web technologies in Germany.

In fact, these operations were already possible in LINQ before, but only in a cumbersome way via grouping with GroupJoin() and SelectMany() as well as DefaultIfEmpty(). The new methods LeftJoin() and RightJoin() simplify their use, as the following code shows using the example of a join between the Company and Website classes:

public void LeftRightJoin()
{
 CUI.Demo();
 
 Company[] companies =
 [
   new Company{ ID = 1, Name = "www.IT-Visions.de" },
   new Company{ ID = 2, Name = "Software & Support" },
   new Company{ ID = 3, Name = "Heise Gruppe GmbH & Co. KG" },
   new Company{ ID = 4, Name = "Startup i.Gr." } // hat noch keine Website
 ];
 
 Website[] websites =
 [
   new Website{ CompanyID = 1, URL = "www.IT-Visions.de" },
   new Website{ CompanyID = 1, URL = "www.dotnet10.de" },
   new Website{ CompanyID = 2, URL = "www.entwickler.de" },
   new Website{ URL = "www.Microsoft.com" }, // Diese kleine ;-) Firma ist noch nicht angelegt...
   new Website{ CompanyID = 3, URL = "www.heise.de" },
 ];
 
 // ### LeftJOIN ALT ############################################################################
 
 CUI.H2("--- Alle Firmen mit ggf. vorhandenen Websites via LeftJoin ALT (GroupJoin+SelectMany) seit .NET Framework 3.5 ---");
 
 var AllCompaniesWithWebsitesSetOld = companies
     .GroupJoin(websites,
                c => c.ID,
                w => w.CompanyID,
                (c, websites) => new { Company = c, Websites = websites })
     .SelectMany(
         x => x.Websites.DefaultIfEmpty(),  // Falls keine Website existiert, wird `null` verwendet
         (c, w) => new WebsiteWithCompany
         {
          Name = c.Company.Name,
          URL = w.URL,   // Falls `w` null ist, bleibt URL null
          City = c.Company.City
         });
 
 foreach (var item in AllCompaniesWithWebsitesSetOld)
 {
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City : "- keine Firma -").Trim() + " -> " + (item.URL ?? "- keine URL -"));
 }
 
 // ### LeftJOIN NEU ############################################################################
 
 CUI.H2("--- Alle Firmen mit ggf. vorhandenen Websites via LeftJoin NEU ab .NET 10.0 ---");
 var AllCompaniesWithWebsitesSet = companies.LeftJoin(websites,
  e => e.ID,
  e => e.CompanyID,
  (c, w) => new WebsiteWithCompany { Name = c.Name, City = c.City, URL = w.URL }
  );
 
 foreach (var item in AllCompaniesWithWebsitesSet)
 {
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City : "- keine Firma -").Trim() + " -> " + (item.URL ?? "- keine URL -"));
 }
 
 // ### RightJoin ALT ############################################################################
 
 CUI.H2("--- Alle Websites mit ggf. vorhandenen Firmen via RightJoin ALT (GroupJoin+SelectMany) seit .NET Framework 3.5  ---");
 var WebsiteWithCompanySetOLD = websites
     .GroupJoin(companies,
                w => w.CompanyID,
                c => c.ID,
                (w, companies) => new { Website = w, Companies = companies })
     .SelectMany(
         x => x.Companies.DefaultIfEmpty(),  // Falls kein Unternehmen existiert -> null
         (w, c) => new WebsiteWithCompany
         {
          Name = c.Name,  // Falls `c` null ist, bleibt `Name` null
          City = c.City,   // Falls `c` null ist, bleibt `City` null
          URL = w.Website.URL
         });
 
 foreach (var item in WebsiteWithCompanySetOLD)
 {
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City : "- keine Firma -").Trim() + " -> " + (item.URL ?? "- keine URL -"));
 }
 
 // ### RightJoin NEU ############################################################################
 
 CUI.H2("--- Alle Websites mit ggf. vorhandenen Firmen via RightJoin NEU ab .NET 10.0 ---");
 var WebsiteWithCompanySet = companies.RightJoin(websites,
     e => e.ID,
     e => e.CompanyID,
     (c, w) => new WebsiteWithCompany { Name = c.Name, City = c.City, URL = w.URL }
  );
 
 foreach (var item in WebsiteWithCompanySet)
 {
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City : "- keine Firma -").Trim() + " -> " + (item.URL ?? "- keine URL -"));
 }
 
 // ### Zum Vergleich: Inner Join, den es seit .NET Framework 3.5 gibt
 CUI.H2("--- Alle Firmen, die Websites haben, via InnerJoin seit .NET Framework 3.5 ---");
 var CompaniesWithWebsitesSet = companies.Join(websites,
                                        c => c.ID,
                                        w => w.CompanyID,
                                        (c, w) => new WebsiteWithCompany
                                        {
                                         Name = c.Name,
                                         URL = w.URL,
                                         City = c.City
                                        });
 foreach (var item in CompaniesWithWebsitesSet)
 {
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City : "- keine Firma -").Trim() + " -> " + (item.URL ?? "- keine URL -"));
 }
}

The code produces this output (Fig. 1).

Videos by heise

According to Microsoft, the new operators are more performant than the previous operator combination.

Performance of LeftJoin() compared to the previous operator combination (Fig. 2)

(Image: Microsoft)

(mho)

Don't miss any news – follow us on Facebook, LinkedIn or Mastodon.

This article was originally published in German. It was translated with technical assistance and editorially reviewed before publication.