protected override Expression VisitSubQuery(SubQueryExpression expression) { var oldParentSelector = _parentSelector; _parentSelector = expression.QueryModel.SelectClause.Selector; expression.QueryModel.TransformExpressions(Visit); _parentSelector = oldParentSelector; var querySourceReferenceExpression = expression.QueryModel.SelectClause.Selector as QuerySourceReferenceExpression; if (querySourceReferenceExpression != null) { var querySourceTracingExpressionVisitor = new QuerySourceTracingExpressionVisitor(); var resultQuerySource = querySourceTracingExpressionVisitor .FindResultQuerySourceReferenceExpression( _parentSelector, querySourceReferenceExpression.ReferencedQuerySource); if ((resultQuerySource == null) && !(expression.QueryModel.ResultOperators.LastOrDefault() is OfTypeResultOperator)) { _querySources[querySourceReferenceExpression.ReferencedQuerySource]--; } } return(expression); }
private void ApplyIncludesForEagerLoadedNavigations(QuerySourceReferenceExpression querySourceReferenceExpression, QueryModel queryModel) { if (_querySourceTracingExpressionVisitor .FindResultQuerySourceReferenceExpression( queryModel.SelectClause.Selector, querySourceReferenceExpression.ReferencedQuerySource) != null) { var entityType = _queryCompilationContext.Model.FindEntityType(querySourceReferenceExpression.Type); if (entityType != null) { var stack = new Stack <INavigation>(); WalkNavigations(querySourceReferenceExpression, entityType, stack); } } }
private void AdjustForResultOperators(QueryModel queryModel) { var isSubQuery = _queryModelStack.Count > 0; var finalResultOperator = queryModel.ResultOperators.LastOrDefault(); if (isSubQuery && finalResultOperator is GroupResultOperator groupResultOperator) { if (!(groupResultOperator.KeySelector is MemberInitExpression)) { // This is to compensate for the fact that we have to demote querysources found in selector for GroupBy // TODO: See #11215 foreach (var querySource in TraverseQuerySources(queryModel.SelectClause.Selector)) { DemoteQuerySource(querySource); } if (groupResultOperator.ElementSelector is QuerySourceReferenceExpression qsre) { DemoteQuerySource(qsre.ReferencedQuerySource); } return; } } var referencedQuerySource = queryModel.SelectClause.Selector.TryGetReferencedQuerySource() ?? queryModel.MainFromClause.FromExpression.TryGetReferencedQuerySource(); // The selector may not have been a QSRE but this query model may still have something that needs adjusted. // Example: // context.Orders.GroupBy(o => o.CustomerId).Select(g => new { g.Key, g.Sum(o => o.TotalAmount) }) // The g.Sum(...) will result in a subquery model like { from Order o in [g] select o.TotalAmount => Sum() }. // In that case we need to ensure that the referenced query source [g] is demoted. if (referencedQuerySource == null) { return; } // If the GroupResultOperator is not last (as captured by above) // then we promote first GroupResultOperator to fall through streaming group by var firstGroupResultOperator = queryModel.ResultOperators.OfType <GroupResultOperator>().FirstOrDefault(); if (firstGroupResultOperator != null) { PromoteQuerySource(firstGroupResultOperator); } var unreachableFromParentSelector = isSubQuery && _querySourceTracingExpressionVisitor .FindResultQuerySourceReferenceExpression( _queryModelStack.Peek().SelectClause.Selector, referencedQuerySource) == null; if (finalResultOperator is SingleResultOperator || finalResultOperator is FirstResultOperator || finalResultOperator is LastResultOperator) { // If not a subquery or if reachable from the parent selector // we would not want to fall through to one of the next blocks. if (unreachableFromParentSelector) { DemoteQuerySourceAndUnderlyingFromClause(referencedQuerySource); } return; } if (ConvergesToSingleValue(queryModel) // This is to preserve QuerySource from MainFromClause when selector is ConstantExpression // Since we would cause client evaluation from ConstantExpression. // TODO: See #11215 && !(queryModel.SelectClause.Selector is ConstantExpression)) { // This is a top-level query that was not Single/First/Last // but returns a single/scalar value (Avg/Min/Max/etc.) // or a subquery that belongs to some outer-level query that returns // a single or scalar value. The referenced query source should be // re-promoted later if necessary. // For top-level Contains we cannot translate it since Item is not Expression if (!isSubQuery && finalResultOperator is ContainsResultOperator containsResultOperator) { return; } // If we are selecting from Grouping source but it is not GroupByAggregate Query // then do not demote Grouping source since we still need to create groups. if (referencedQuerySource.ItemType.IsGrouping() && !IsGroupByAggregateSubQuery(queryModel)) { return; } DemoteQuerySourceAndUnderlyingFromClause(referencedQuerySource); return; } if (isSubQuery && (unreachableFromParentSelector || finalResultOperator is DefaultIfEmptyResultOperator)) { DemoteQuerySourceAndUnderlyingFromClause(referencedQuerySource); } }
/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> protected override Expression VisitSubQuery(SubQueryExpression expression) { var oldParentSelector = _selector; var oldQueryModel = _queryModel; _selector = expression.QueryModel.SelectClause.Selector; _queryModel = expression.QueryModel; _queryModel.TransformExpressions(Visit); _selector = oldParentSelector; _queryModel = oldQueryModel; var querySourceReferenceExpression = expression.QueryModel.SelectClause.Selector as QuerySourceReferenceExpression; if (querySourceReferenceExpression != null) { var querySourceTracingExpressionVisitor = new QuerySourceTracingExpressionVisitor(); if (expression.QueryModel.ResultOperators.LastOrDefault() is DefaultIfEmptyResultOperator) { var underlyingQuerySource = (((querySourceReferenceExpression.ReferencedQuerySource as MainFromClause) ?.FromExpression as QuerySourceReferenceExpression) ?.ReferencedQuerySource as GroupJoinClause)?.JoinClause; if (underlyingQuerySource != null) { AddQuerySource(underlyingQuerySource); } } var resultQuerySource = querySourceTracingExpressionVisitor .FindResultQuerySourceReferenceExpression( _selector, querySourceReferenceExpression.ReferencedQuerySource); if ((resultQuerySource == null) && !(expression.QueryModel.ResultOperators.LastOrDefault() is OfTypeResultOperator)) { _querySources[querySourceReferenceExpression.ReferencedQuerySource]--; } foreach (var sourceExpression in _queryModel.ResultOperators.Select(SetResultOperationSourceExpression).Where(e => e != null)) { if (sourceExpression.Equals(expression)) { var parentQuerySource = _selector as QuerySourceReferenceExpression; if ((parentQuerySource != null) && (_querySources[parentQuerySource.ReferencedQuerySource] > 0) && (parentQuerySource.Type == querySourceReferenceExpression.Type)) { _querySources[querySourceReferenceExpression.ReferencedQuerySource]++; } } } } return(expression); }
private void AdjustForResultOperators(QueryModel queryModel) { var referencedQuerySource = queryModel.SelectClause.Selector.TryGetReferencedQuerySource() ?? queryModel.MainFromClause.FromExpression.TryGetReferencedQuerySource(); // The selector may not have been a QSRE but this query model may still have something that needs adjusted. // Example: // context.Orders.GroupBy(o => o.CustomerId).Select(g => new { g.Key, g.Sum(o => o.TotalAmount) }) // The g.Sum(...) will result in a subquery model like { from Order o in [g] select o.TotalAmount => Sum() }. // In that case we need to ensure that the referenced query source [g] is demoted. if (referencedQuerySource == null) { return; } var isSubQuery = _queryModelStack.Count > 0; var finalResultOperator = queryModel.ResultOperators.LastOrDefault(); if (isSubQuery && finalResultOperator is GroupResultOperator) { // These two lines should be uncommented to implement GROUP BY translation. //DemoteQuerySource(referencedQuerySource); //DemoteQuerySource(groupResultOperator); return; } var unreachableFromParentSelector = isSubQuery && _querySourceTracingExpressionVisitor .FindResultQuerySourceReferenceExpression( _queryModelStack.Peek().SelectClause.Selector, referencedQuerySource) == null; if (finalResultOperator is SingleResultOperator || finalResultOperator is FirstResultOperator || finalResultOperator is LastResultOperator) { // If not a subquery or if reachable from the parent selector // we would not want to fall through to one of the next blocks. if (unreachableFromParentSelector) { DemoteQuerySourceAndUnderlyingFromClause(referencedQuerySource); } return; } if (ConvergesToSingleValue(queryModel)) { // This is a top-level query that was not Single/First/Last // but returns a single/scalar value (Avg/Min/Max/etc.) // or a subquery that belongs to some outer-level query that returns // a single or scalar value. The referenced query source should be // re-promoted later if necessary. // For top-level Contains we cannot translate it since Item is not Expression if (!isSubQuery && finalResultOperator is ContainsResultOperator containsResultOperator) { return; } DemoteQuerySourceAndUnderlyingFromClause(referencedQuerySource); return; } if (isSubQuery && (unreachableFromParentSelector || finalResultOperator is DefaultIfEmptyResultOperator)) { DemoteQuerySourceAndUnderlyingFromClause(referencedQuerySource); } }