/// <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 VisitQuerySourceReference(
            QuerySourceReferenceExpression expression)
        {
            var sequenceType = expression.Type.TryGetSequenceType();

            var entityType
                = _queryCompilationContext.FindEntityType(expression.ReferencedQuerySource)
                  ?? ((sequenceType != null
                          ? _model.FindEntityType(sequenceType)
                          : null)
                      ?? _model.FindEntityType(expression.Type));

            if (entityType != null &&
                !entityType.IsQueryType)
            {
                _entityTrackingInfos.Add(
                    _entityTrackingInfoFactory
                    .Create(_queryCompilationContext, expression, entityType));
            }

            return(expression);
        }
        /// <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>
        public override void VisitResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, int index)
        {
            if (resultOperator is ValueFromSequenceResultOperatorBase &&
                !(resultOperator is FirstResultOperator) &&
                !(resultOperator is SingleResultOperator) &&
                !(resultOperator is LastResultOperator) &&
                !queryModel.ResultOperators
                .Any(r => r is TakeResultOperator || r is SkipResultOperator))
            {
                for (var i = queryModel.BodyClauses.Count - 1; i >= 0; i--)
                {
                    if (queryModel.BodyClauses[i] is OrderByClause)
                    {
                        queryModel.BodyClauses.RemoveAt(i);
                    }
                }
            }

            if (resultOperator is OfTypeResultOperator ofTypeOperator)
            {
                var searchedItemType = ofTypeOperator.SearchedItemType;
                if (searchedItemType == queryModel.MainFromClause.ItemType)
                {
                    queryModel.ResultOperators.RemoveAt(index);
                }
                else
                {
                    var entityType = _queryCompilationContext.Model.FindEntityType(searchedItemType)
                                     ?? _queryCompilationContext.FindEntityType(queryModel.MainFromClause);

                    if (entityType != null)
                    {
                        var oldQuerySource = queryModel.MainFromClause;

                        if (((oldQuerySource.FromExpression as ConstantExpression)?.Value as IQueryable)?.Provider
                            is IAsyncQueryProvider entityQueryProvider)
                        {
                            queryModel.ResultOperators.RemoveAt(index);

                            var newMainFromClause
                                = new MainFromClause(
                                      oldQuerySource.ItemName,
                                      entityType.ClrType,
                                      entityQueryProvider.CreateEntityQueryableExpression(entityType.ClrType));

                            queryModel.MainFromClause = newMainFromClause;

                            _queryCompilationContext.AddOrUpdateMapping(newMainFromClause, entityType);

                            UpdateQuerySourceMapping(
                                queryModel,
                                oldQuerySource,
                                new QuerySourceReferenceExpression(newMainFromClause));
                        }
                    }
                }
            }

            ProcessSetResultOperator(resultOperator);

            TryOptimizeContains(resultOperator, queryModel);

            base.VisitResultOperator(resultOperator, queryModel, index);
        }
            protected void Compile(
                QueryCompilationContext queryCompilationContext,
                QueryModel queryModel,
                bool trackingQuery,
                bool asyncQuery,
                ref int collectionIncludeId,
                QuerySourceReferenceExpression targetQuerySourceReferenceExpression)
            {
                var entityParameter
                    = Expression.Parameter(targetQuerySourceReferenceExpression.Type, name: "entity");

                var propertyExpressions = new List <Expression>();
                var blockExpressions    = new List <Expression>();

                var entityType
                    = queryCompilationContext.FindEntityType(targetQuerySourceReferenceExpression.ReferencedQuerySource)
                      ?? queryCompilationContext.Model.FindEntityType(entityParameter.Type);

                if (entityType.FindPrimaryKey() == null)
                {
                    trackingQuery = false;
                }

                if (trackingQuery)
                {
                    blockExpressions.Add(
                        Expression.Call(
                            Expression.Property(
                                EntityQueryModelVisitor.QueryContextParameter,
                                nameof(QueryContext.QueryBuffer)),
                            _queryBufferStartTrackingMethodInfo,
                            entityParameter,
                            Expression.Constant(entityType)));
                }

                var includedIndex = 0;

                // ReSharper disable once LoopCanBeConvertedToQuery
                foreach (var includeLoadTreeNode in Children)
                {
                    blockExpressions.Add(
                        includeLoadTreeNode.Compile(
                            queryCompilationContext,
                            targetQuerySourceReferenceExpression,
                            entityParameter,
                            propertyExpressions,
                            trackingQuery,
                            asyncQuery,
                            ref includedIndex,
                            ref collectionIncludeId));
                }

                if (blockExpressions.Count > 1 ||
                    blockExpressions.Count == 1 &&
                    !trackingQuery)
                {
                    AwaitTaskExpressions(asyncQuery, blockExpressions);

                    var includeExpression
                        = blockExpressions.Last().Type == typeof(Task)
                            ? new TaskBlockingExpressionVisitor()
                          .Visit(
                              Expression.Call(
                                  _includeAsyncMethodInfo
                                  .MakeGenericMethod(targetQuerySourceReferenceExpression.Type),
                                  EntityQueryModelVisitor.QueryContextParameter,
                                  targetQuerySourceReferenceExpression,
                                  Expression.NewArrayInit(typeof(object), propertyExpressions),
                                  Expression.Lambda(
                                      Expression.Block(blockExpressions),
                                      EntityQueryModelVisitor.QueryContextParameter,
                                      entityParameter,
                                      _includedParameter,
                                      CancellationTokenParameter),
                                  CancellationTokenParameter))
                            : Expression.Call(
                              _includeMethodInfo.MakeGenericMethod(targetQuerySourceReferenceExpression.Type),
                              EntityQueryModelVisitor.QueryContextParameter,
                              targetQuerySourceReferenceExpression,
                              Expression.NewArrayInit(typeof(object), propertyExpressions),
                              Expression.Lambda(
                                  Expression.Block(typeof(void), blockExpressions),
                                  EntityQueryModelVisitor.QueryContextParameter,
                                  entityParameter,
                                  _includedParameter));

                    ApplyIncludeExpressionsToQueryModel(
                        queryModel, targetQuerySourceReferenceExpression, includeExpression);
                }
            }
Example #4
0
        /// <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 VisitBinary(BinaryExpression binaryExpression)
        {
            Check.NotNull(binaryExpression, nameof(binaryExpression));

            var newBinaryExpression = (BinaryExpression)base.VisitBinary(binaryExpression);

            if (binaryExpression.NodeType == ExpressionType.Equal ||
                binaryExpression.NodeType == ExpressionType.NotEqual)
            {
                var isLeftNullConstant  = newBinaryExpression.Left.IsNullConstantExpression();
                var isRightNullConstant = newBinaryExpression.Right.IsNullConstantExpression();

                if (isLeftNullConstant && isRightNullConstant)
                {
                    return(newBinaryExpression);
                }

                var isNullComparison  = isLeftNullConstant || isRightNullConstant;
                var nonNullExpression = isLeftNullConstant ? newBinaryExpression.Right : newBinaryExpression.Left;

                var qsre = nonNullExpression as QuerySourceReferenceExpression;

                var leftProperties = MemberAccessBindingExpressionVisitor.GetPropertyPath(
                    newBinaryExpression.Left, _queryCompilationContext, out var leftNavigationQsre);

                var rightProperties = MemberAccessBindingExpressionVisitor.GetPropertyPath(
                    newBinaryExpression.Right, _queryCompilationContext, out var rightNavigationQsre);

                if (isNullComparison)
                {
                    var nonNullNavigationQsre = isLeftNullConstant ? rightNavigationQsre : leftNavigationQsre;
                    var nonNullproperties     = isLeftNullConstant ? rightProperties : leftProperties;

                    if (IsCollectionNavigation(nonNullNavigationQsre, nonNullproperties))
                    {
                        // collection navigation is only null if its parent entity is null (null propagation thru navigation)
                        // it is probable that user wanted to see if the collection is (not) empty, log warning suggesting to use Any() instead.
                        _queryCompilationContext.Logger
                        .PossibleUnintendedCollectionNavigationNullComparisonWarning(nonNullproperties);

                        var callerExpression = CreateCollectionCallerExpression(nonNullNavigationQsre, nonNullproperties);

                        return(Visit(Expression.MakeBinary(newBinaryExpression.NodeType, callerExpression, Expression.Constant(null))));
                    }
                }

                var collectionNavigationComparison = TryRewriteCollectionNavigationComparison(
                    newBinaryExpression.Left,
                    newBinaryExpression.Right,
                    newBinaryExpression.NodeType,
                    leftNavigationQsre,
                    rightNavigationQsre,
                    leftProperties,
                    rightProperties);

                if (collectionNavigationComparison != null)
                {
                    return(collectionNavigationComparison);
                }

                // If a reference navigation is being compared to null then don't rewrite
                if (isNullComparison &&
                    qsre == null)
                {
                    return(newBinaryExpression);
                }

                var entityType = _queryCompilationContext.Model.FindEntityType(nonNullExpression.Type);
                if (entityType == null)
                {
                    if (qsre != null)
                    {
                        entityType = _queryCompilationContext.FindEntityType(qsre.ReferencedQuerySource);
                    }
                    else
                    {
                        var properties = MemberAccessBindingExpressionVisitor.GetPropertyPath(
                            nonNullExpression, _queryCompilationContext, out qsre);
                        if (properties.Count > 0 &&
                            properties[properties.Count - 1] is INavigation navigation)
                        {
                            entityType = navigation.GetTargetType();
                        }
                    }
                }

                if (entityType != null)
                {
                    return(CreateKeyComparison(
                               entityType,
                               newBinaryExpression.Left,
                               newBinaryExpression.Right,
                               newBinaryExpression.NodeType,
                               isLeftNullConstant,
                               isRightNullConstant,
                               isNullComparison));
                }
            }

            return(newBinaryExpression);
        }
Example #5
0
        /// <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 VisitBinary(BinaryExpression binaryExpression)
        {
            Check.NotNull(binaryExpression, nameof(binaryExpression));

            var newBinaryExpression = (BinaryExpression)base.VisitBinary(binaryExpression);

            if (binaryExpression.NodeType == ExpressionType.Equal ||
                binaryExpression.NodeType == ExpressionType.NotEqual)
            {
                var isLeftNullConstant  = newBinaryExpression.Left.IsNullConstantExpression();
                var isRightNullConstant = newBinaryExpression.Right.IsNullConstantExpression();

                if (isLeftNullConstant && isRightNullConstant)
                {
                    return(newBinaryExpression);
                }

                var isNullComparison  = isLeftNullConstant || isRightNullConstant;
                var nonNullExpression = isLeftNullConstant ? newBinaryExpression.Right : newBinaryExpression.Left;

                var qsre = nonNullExpression as QuerySourceReferenceExpression;
                // If a navigation being compared to null then don't rewrite
                if (isNullComparison &&
                    qsre == null)
                {
                    return(newBinaryExpression);
                }

                var entityType = _queryCompilationContext.Model.FindEntityType(nonNullExpression.Type);
                if (entityType == null)
                {
                    if (qsre != null)
                    {
                        entityType = _queryCompilationContext.FindEntityType(qsre.ReferencedQuerySource);
                    }
                    else
                    {
                        var properties = MemberAccessBindingExpressionVisitor.GetPropertyPath(
                            nonNullExpression, _queryCompilationContext, out qsre);
                        if (properties.Count > 0 &&
                            properties[properties.Count - 1] is INavigation navigation)
                        {
                            entityType = navigation.GetTargetType();
                        }
                    }
                }

                if (entityType != null)
                {
                    var primaryKeyProperties = entityType.FindPrimaryKey().Properties;

                    var newLeftExpression = isLeftNullConstant
                        ? Expression.Constant(null, typeof(object))
                        : CreateKeyAccessExpression(newBinaryExpression.Left, primaryKeyProperties, isNullComparison);

                    var newRightExpression = isRightNullConstant
                        ? Expression.Constant(null, typeof(object))
                        : CreateKeyAccessExpression(newBinaryExpression.Right, primaryKeyProperties, isNullComparison);

                    return(Expression.MakeBinary(newBinaryExpression.NodeType, newLeftExpression, newRightExpression));
                }
            }

            return(newBinaryExpression);
        }