private Expression Rewrite( int correlatedCollectionIndex, QueryModel collectionQueryModel, INavigation navigation, bool trackingQuery, QuerySourceReferenceExpression originQuerySource, bool forceListResult) { var querySourceReferenceFindingExpressionTreeVisitor = new QuerySourceReferenceFindingExpressionTreeVisitor(); var originalCorrelationPredicate = collectionQueryModel.BodyClauses.OfType <WhereClause>().Single(c => c.Predicate is NullConditionalEqualExpression); collectionQueryModel.BodyClauses.Remove(originalCorrelationPredicate); originalCorrelationPredicate.TransformExpressions(querySourceReferenceFindingExpressionTreeVisitor.Visit); var parentQuerySourceReferenceExpression = querySourceReferenceFindingExpressionTreeVisitor.QuerySourceReferenceExpression; querySourceReferenceFindingExpressionTreeVisitor = new QuerySourceReferenceFindingExpressionTreeVisitor(); querySourceReferenceFindingExpressionTreeVisitor.Visit(((NullConditionalEqualExpression)originalCorrelationPredicate.Predicate).InnerKey); var currentKey = BuildKeyAccess(navigation.ForeignKey.Properties, querySourceReferenceFindingExpressionTreeVisitor.QuerySourceReferenceExpression); // PK of the parent qsre var originKey = BuildKeyAccess(_queryCompilationContext.Model.FindEntityType(originQuerySource.Type).FindPrimaryKey().Properties, originQuerySource); // principal side of the FK relationship between parent and this collection var outerKey = BuildKeyAccess(navigation.ForeignKey.PrincipalKey.Properties, parentQuerySourceReferenceExpression); var parentQuerySource = parentQuerySourceReferenceExpression.ReferencedQuerySource; // ordering priority for parent: // - user specified orderings // - parent PK // - principal side of the FK between parent and child // ordering priority for child: // - user specified orderings on parent (from join) // - parent PK (from join) // - dependent side of the FK between parent and child // - customer specified orderings on child var parentOrderings = new List <Ordering>(); var exisingParentOrderByClause = _parentQueryModel.BodyClauses.OfType <OrderByClause>().LastOrDefault(); if (exisingParentOrderByClause != null) { parentOrderings.AddRange(exisingParentOrderByClause.Orderings); } var originEntityType = _queryCompilationContext.Model.FindEntityType(originQuerySource.Type); foreach (var property in originEntityType.FindPrimaryKey().Properties) { TryAddPropertyToOrderings(property, originQuerySource, parentOrderings); } foreach (var property in navigation.ForeignKey.PrincipalKey.Properties) { TryAddPropertyToOrderings(property, parentQuerySourceReferenceExpression, parentOrderings); } _parentOrderings.AddRange(parentOrderings); // if selector contains multiple correlated collections, visiting the first one changes that collections QM (changing it's type) // which makes the parent QM inconsistent temporarily. QM's type is different but the CorrelateCollections method that fixes the result type // is not part of the QM and it's added only when the entire Selector is replaced - i.e. after all it's components have been visited // since when we clone the parent QM, we don't care about it's original selector anyway (it's being discarded) // we avoid cloning the selector in the first place and avoid all the potential problem with temporarily mismatched types of the subqueries inside var parentSelectClause = _parentQueryModel.SelectClause; _parentQueryModel.SelectClause = new SelectClause(Expression.Default(parentSelectClause.Selector.Type)); var querySourceMapping = new QuerySourceMapping(); var clonedParentQueryModel = _parentQueryModel.Clone(querySourceMapping); _parentQueryModel.SelectClause = parentSelectClause; _queryCompilationContext.UpdateMapping(querySourceMapping); _queryCompilationContext.CloneAnnotations(querySourceMapping, clonedParentQueryModel); var clonedParentQuerySourceReferenceExpression = (QuerySourceReferenceExpression)querySourceMapping.GetExpression(parentQuerySource); var clonedParentQuerySource = clonedParentQuerySourceReferenceExpression.ReferencedQuerySource; var parentItemName = parentQuerySource.HasGeneratedItemName() ? navigation.DeclaringEntityType.DisplayName()[0].ToString().ToLowerInvariant() : parentQuerySource.ItemName; collectionQueryModel.MainFromClause.ItemName = $"{parentItemName}.{navigation.Name}"; var collectionQuerySourceReferenceExpression = new QuerySourceReferenceExpression(collectionQueryModel.MainFromClause); var subQueryProjection = new List <Expression>(); subQueryProjection.AddRange(parentOrderings.Select(o => CloningExpressionVisitor.AdjustExpressionAfterCloning(o.Expression, querySourceMapping))); var joinQuerySourceReferenceExpression = CreateJoinToParentQuery( clonedParentQueryModel, clonedParentQuerySourceReferenceExpression, collectionQuerySourceReferenceExpression, navigation.ForeignKey, collectionQueryModel, subQueryProjection); ApplyParentOrderings( parentOrderings, clonedParentQueryModel, querySourceMapping); LiftOrderBy( joinQuerySourceReferenceExpression, clonedParentQueryModel, collectionQueryModel); clonedParentQueryModel.SelectClause.Selector = Expression.New( MaterializedAnonymousObject.AnonymousObjectCtor, Expression.NewArrayInit( typeof(object), subQueryProjection.Select(e => Expression.Convert(e, typeof(object))))); clonedParentQueryModel.ResultTypeOverride = typeof(IQueryable <>).MakeGenericType(clonedParentQueryModel.SelectClause.Selector.Type); var newOriginKey = CloningExpressionVisitor .AdjustExpressionAfterCloning(originKey, querySourceMapping); var newOriginKeyElements = ((NewArrayExpression)(((NewExpression)newOriginKey).Arguments[0])).Expressions; var remappedOriginKeyElements = RemapOriginKeyExpressions(newOriginKeyElements, joinQuerySourceReferenceExpression, subQueryProjection); var tupleCtor = typeof(Tuple <, ,>).MakeGenericType( collectionQueryModel.SelectClause.Selector.Type, typeof(MaterializedAnonymousObject), typeof(MaterializedAnonymousObject)).GetConstructors().FirstOrDefault(); var navigationParameter = Expression.Parameter(typeof(INavigation), "n"); var correlateSubqueryMethod = _queryCompilationContext.IsAsyncQuery ? _correlateSubqueryAsyncMethodInfo : _correlateSubqueryMethodInfo; Expression resultCollectionFactoryExpressionBody; if (forceListResult || navigation.ForeignKey.DeclaringEntityType.ClrType != collectionQueryModel.SelectClause.Selector.Type) { var resultCollectionType = typeof(List <>).MakeGenericType(collectionQueryModel.SelectClause.Selector.Type); var resultCollectionCtor = resultCollectionType.GetTypeInfo().GetDeclaredConstructor(new Type[] { }); correlateSubqueryMethod = correlateSubqueryMethod.MakeGenericMethod( collectionQueryModel.SelectClause.Selector.Type, typeof(List <>).MakeGenericType(collectionQueryModel.SelectClause.Selector.Type)); resultCollectionFactoryExpressionBody = Expression.New(resultCollectionCtor); trackingQuery = false; } else { correlateSubqueryMethod = correlateSubqueryMethod.MakeGenericMethod( collectionQueryModel.SelectClause.Selector.Type, navigation.GetCollectionAccessor().CollectionType); resultCollectionFactoryExpressionBody = Expression.Convert( Expression.Call( Expression.Call(_getCollectionAccessorMethodInfo, navigationParameter), _createCollectionMethodInfo), navigation.GetCollectionAccessor().CollectionType); } var resultCollectionFactoryExpression = Expression.Lambda( resultCollectionFactoryExpressionBody, navigationParameter); collectionQueryModel.SelectClause.Selector = Expression.New( tupleCtor, new Expression[] { collectionQueryModel.SelectClause.Selector, currentKey, Expression.New( MaterializedAnonymousObject.AnonymousObjectCtor, Expression.NewArrayInit( typeof(object), remappedOriginKeyElements)) }); var collectionModelSelectorType = collectionQueryModel.SelectClause.Selector.Type; // Enumerable or OrderedEnumerable collectionQueryModel.ResultTypeOverride = collectionQueryModel.BodyClauses.OfType <OrderByClause>().Any() ? typeof(IOrderedEnumerable <>).MakeGenericType(collectionModelSelectorType) : typeof(IEnumerable <>).MakeGenericType(collectionModelSelectorType); var lambda = (Expression)Expression.Lambda(new SubQueryExpression(collectionQueryModel)); if (_queryCompilationContext.IsAsyncQuery) { lambda = Expression.Convert( lambda, typeof(Func <>).MakeGenericType( typeof(IAsyncEnumerable <>).MakeGenericType(collectionModelSelectorType))); } // since we cloned QM, we need to check if it's query sources require materialization (e.g. TypeIs operation for InMemory) _queryCompilationContext.FindQuerySourcesRequiringMaterialization(_queryModelVisitor, collectionQueryModel); var correlationPredicate = CreateCorrelationPredicate(navigation); var arguments = new List <Expression> { Expression.Constant(correlatedCollectionIndex), Expression.Constant(navigation), resultCollectionFactoryExpression, outerKey, Expression.Constant(trackingQuery), lambda, correlationPredicate }; if (_queryCompilationContext.IsAsyncQuery) { arguments.Add(_cancellationTokenParameter); } var result = Expression.Call( Expression.Property( EntityQueryModelVisitor.QueryContextParameter, nameof(QueryContext.QueryBuffer)), correlateSubqueryMethod, arguments); if (_queryCompilationContext.IsAsyncQuery) { var taskResultExpression = new TaskBlockingExpressionVisitor().Visit(result); return(taskResultExpression); } return(result); }