TL;DR: In .NET 11, a.FullJoin(b, x => x.Key, y => y.Key) returns every matched pair plus the unmatched rows from both sides, as (Outer, Inner) tuples. Join , LeftJoin , RightJoin , and GroupJoin also get overloads with no result selector. .NET 11 is at Release Candidate 2 right now: go-live licensed, but not final yet.
The join you Google every time
You have two lists that should agree and never do. What the system thinks is in stock, and what the stocktake actually counted. You want three answers: what matches, what only the left side has, and what only the right side has.
SQL calls that a full outer join. LINQ never had one, so everybody pastes the same answer:
var left = from s in system join c in counted on s . Sku equals c . Sku into matches from c in matches . DefaultIfEmpty () select ( Record : s , Count : c );
var rightOnly = from c in counted where ! system . Any ( s => s . Sku == c . Sku ) select ( Record : ( StockRecord ?) null , Count : c );
var full = left .…