Example #1
0
        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);
                }
            }
        }
Example #3
0
        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);
        }
Example #5
0
        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);
            }
        }