Search code examples
c#linqouter-join

LINQ: Left Outer Join with multiple conditions


I have two IEnumerables called BaseReportDefinitions and InputReportDefinitions. I need to do a left outer join where i want all the InputReportDefinitions and whichever BaseReportDefinitions that match. Both IEnumberables contain ReportDefinition objects that contain ParentName and ReportName properties that need to be used as the join key. I want to return the ReportDefinition object for each (in the case of BaseReportDefinition entry it may be null) in an anonymous object.

I have seen many examples of linq outer joins and outer joins with a static second condition that often gets put into a where condition but nothing that really uses two conditions fully for the join.


Solution

  • var items = inputReportDefinitions.GroupJoin(
                  baseReportDefinitions,
                  firstSelector => new {
                             firstSelector.ParentName, firstSelector.ReportName
                                       },
                  secondSelector => new {
                             secondSelector.ParentName, secondSelector.ReportName
                                       },
                  (inputReport, baseCollection) => new {inputReport, baseCollection})
                  .SelectMany(grp => grp.baseCollection.DefaultIfEmpty(),
                             (col, baseReport) => new
                                                     {
                                                        Base = baseReport,
                                                        Input = col.inputReport
                                                     });
    

    I believe this ends up being a left outer join. I don't know how to convert this monstrosity to a query statement. I think if you add AsQueryable() to the end it could be used in Linq-to-SQL, but honestly, I have little experience with that.

    EDIT: I figured it out. Much easier to read:

    var otherItems = from i in inputReportDefinitions
                             join b in baseReportDefinitions
                             on new {i.ParentName, i.ReportName} 
                             equals new {b.ParentName, b.ReportName} into other
                             from baseReport in other.DefaultIfEmpty()
                             select new
                                        {
                                            Input = i,
                                            Base = baseReport
                                        };