For this test:
public virtual Task Index_constant(bool async)
=> AssertQuery(
async,
ss => ss.Set<RootEntity>().Where(e => e.RelatedCollection[0].Int == 21),
ss => ss.Set<RootEntity>().Where(e => e.RelatedCollection.Count > 0 && e.RelatedCollection[0].Int == 21));
In preprocessing, before SubqueryMemberPushdownExpressionVisitor we have the following tree:
DbSet<RootEntity>()
.Where(e => e.RelatedCollection
.AsQueryable()
.ElementAt(0).Int == 21)
Afterwards we have:
DbSet<RootEntity>()
.Where(e => e.RelatedCollection
.AsQueryable()
.Select(s => s.Int)
.ElementAt(0) == 21)
In other words, the member access for Int gets "pushed down" to before the ElementAt() call, and transformed to a Select(). For some context, see #30386 (comment).
Trouble is, this makes the tree complicated to process for e.g. simple complex translation: the original tree corresponds very well to simple JSON drilling into the document, where as the transformed one does not, and requires later hackery in the query pipeilne.
See also #30386, where this caused issues in EFCore.PG.