Summary
A join between two entity types that have no navigation connecting them (the EF-377 raw-key join path) builds its lookup fields from the property's bare element name. When a join key is one component of a composite primary key, that property is stored nested under the document id (for example _id.ProductId), so the lookup matches on a field that does not exist and the join silently returns no rows. There is no exception.
Affected versions
Measured on the released v10.0.4 tag. v8.4.4 and v9.1.4 contain the same raw-key lookup construction (verified by reading the code at those tags, not by running them).
In released versions a SINGLE such join is unaffected, because the driver's own join translation builds its own lookup. A CHAIN of two or more joins is affected: every hop's lookup is registered at translation time and emitted by the driver-LINQ bridge, so the wrong field paths reach the server.
Repro
Model: Product (int Id, Name) and LineItem (int OrderId, int ProductId, int Quantity) with a composite key on LineItem of OrderId and ProductId. No navigation between the two types.
modelBuilder.Entity<LineItem>().HasKey(li => new { li.OrderId, li.ProductId }); // Seed: Product 7 "Widget", Product 8 "Gadget"; LineItems (1, 7, qty 2) and (2, 7, qty 5). var rows = db.Set<Product>() .Join(db.Set<LineItem>(), p => p.Id, li => li.ProductId, (p, li) => new { p, li }) .Join(db.Set<Product>(), x => x.li.ProductId, p2 => p2.Id, (x, p2) => new { x.p.Name, x.li.Quantity, P2 = p2.Name }) .ToList(); // Expected: (Widget, 2, Widget) and (Widget, 5, Widget). Actual on v10.0.4: no rows.
Root cause
In MongoQueryableMethodTranslatingExpressionVisitor.RebindInnerShaperToOuterQuery, the navigation-less branch builds the lookup's local and foreign fields with GetElementName(). The navigation-backed branch already uses LookupExpression.GetFieldPath, which prefixes a composite-key component with the id path. The raw-key branch should use the same helper for both fields (including the transitive-hop case, where the local field is prefixed with the prior hop's alias).
Status
- Fixed on the unreleased native-query branch (native-EF-322-Native-LINQ-rebased) in commit 98fd8798, with a differential functional test (NativeCompositeKeyJoinTests) covering single and chained joins, inner and left-outer, in every query mode.
- Still needs a fix on main for the released 8.x, 9.x and 10.x lines. The fix is a two-line change in the raw-key branch; the chained repro above is the regression test.
Related
Not the same as EF-385 (joins over multi-part keys that match on only the first key column, returning too many rows). This bug is a single composite-key component read at the wrong path, returning too few (zero) rows.