示例#1
0
        private bool IsGroupByAggregateSubQuery(QueryModel queryModel)
        {
            if (queryModel.MainFromClause.FromExpression.Type.IsGrouping() &&
                queryModel.BodyClauses.Count == 0 &&
                queryModel.ResultOperators.Count == 1 &&
                !(queryModel.SelectClause.Selector is ConstantExpression) &&
                _aggregateResultOperators.Contains(queryModel.ResultOperators[0].GetType()) &&
                _queryModelStack.Count == 1 &&
                !_queryModelStack.Peek().BodyClauses.OfType <IQuerySource>().Any())
            {
                var groupResultOperator
                    = (GroupResultOperator)
                      ((SubQueryExpression)
                       ((FromClauseBase)queryModel.MainFromClause.FromExpression.TryGetReferencedQuerySource())
                       .FromExpression)
                      .QueryModel.ResultOperators.Last();

                if (MemberAccessBindingExpressionVisitor.GetPropertyPath(
                        queryModel.SelectClause.Selector, _queryModelVisitor.QueryCompilationContext, out var qsre).Count > 0 ||
                    groupResultOperator.ElementSelector is MemberInitExpression ||
                    groupResultOperator.ElementSelector is NewExpression)
                {
                    return(true);
                }
            }

            return(false);
        }
示例#2
0
        private Expression RewriteNullEquality(ExpressionType nodeType, Expression nonNullExpression)
        {
            var properties = MemberAccessBindingExpressionVisitor
                             .GetPropertyPath(nonNullExpression, _queryCompilationContext, out var qsre);

            if (properties.Count > 0 &&
                properties[properties.Count - 1] is INavigation lastNavigation &&
                lastNavigation.IsCollection())
            {
                // 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(properties);

                return(Visit(
                           Expression.MakeBinary(
                               nodeType,
                               CreateNavigationCaller(qsre, properties),
                               Expression.Constant(null))));
            }

            if (IsInvalidSubQueryExpression(nonNullExpression))
            {
                return(null);
            }

            var entityType = _model.FindEntityType(nonNullExpression.Type)
                             ?? GetEntityType(properties, qsre);

            if (entityType == null ||
                entityType.IsOwned())
            {
                return(null);
            }

            var keyProperties = entityType.FindPrimaryKey().Properties;
            var nullCount     = keyProperties.Count;

            Expression keyAccessExpression;

            // Skipping composite key with subquery since it requires to copy subquery
            // which would cause same subquery to be visited twice
            if (nullCount > 1 &&
                nonNullExpression.RemoveConvert() is SubQueryExpression)
            {
                return(null);
            }

            if (properties.Count > 0 &&
                properties[properties.Count - 1] is INavigation navigation2 &&
                navigation2.IsDependentToPrincipal())
            {
                keyAccessExpression = CreateKeyAccessExpression(
                    CreateNavigationCaller(qsre, properties),
                    navigation2.ForeignKey.Properties,
                    nullComparison: false);
                nullCount = navigation2.ForeignKey.Properties.Count;
            }
示例#3
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 VisitMethodCall(MethodCallExpression methodCallExpression)
        {
            var newMethodCallExpression = (MethodCallExpression)base.VisitMethodCall(methodCallExpression);

            Expression newLeftExpression  = null;
            Expression newRightExpression = null;

            if (newMethodCallExpression.Method.Name == nameof(object.Equals) &&
                newMethodCallExpression.Object != null &&
                newMethodCallExpression.Arguments.Count == 1)
            {
                newLeftExpression  = newMethodCallExpression.Object;
                newRightExpression = newMethodCallExpression.Arguments[0];
            }

            if (newMethodCallExpression.Method.Equals(_objectEqualsMethodInfo))
            {
                newLeftExpression  = newMethodCallExpression.Arguments[0];
                newRightExpression = newMethodCallExpression.Arguments[1];
            }

            if (newLeftExpression != null &&
                newRightExpression != null)
            {
                var leftRootEntityType  = _queryCompilationContext.Model.FindEntityType(newLeftExpression.Type)?.RootType();
                var rightRootEntityType = _queryCompilationContext.Model.FindEntityType(newRightExpression.Type)?.RootType();
                if (leftRootEntityType != null &&
                    leftRootEntityType == rightRootEntityType)
                {
                    return(Visit(Expression.Equal(newLeftExpression, newRightExpression)));
                }

                var isLeftNullConstant  = newLeftExpression.IsNullConstantExpression();
                var isRightNullConstant = newRightExpression.IsNullConstantExpression();

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

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

                if ((isLeftNullConstant || IsCollectionNavigation(leftNavigationQsre, leftProperties)) &&
                    (isRightNullConstant || IsCollectionNavigation(rightNavigationQsre, rightProperties)))
                {
                    return(Visit(Expression.Equal(newLeftExpression, newRightExpression)));
                }
            }

            return(newMethodCallExpression);
        }
示例#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 VisitMethodCall(MethodCallExpression methodCallExpression)
        {
            var newMethodCallExpression = (MethodCallExpression)base.VisitMethodCall(methodCallExpression);

            Expression newLeftExpression  = null;
            Expression newRightExpression = null;

            if (newMethodCallExpression.Method.Name == nameof(object.Equals) &&
                newMethodCallExpression.Object != null &&
                newMethodCallExpression.Arguments.Count == 1)
            {
                newLeftExpression  = newMethodCallExpression.Object;
                newRightExpression = newMethodCallExpression.Arguments[0];
            }

            if (newMethodCallExpression.Method.Equals(_objectEqualsMethodInfo))
            {
                newLeftExpression  = newMethodCallExpression.Arguments[0];
                newRightExpression = newMethodCallExpression.Arguments[1];
            }

            if (newLeftExpression != null &&
                newRightExpression != null)
            {
                var isLeftNullConstant  = newLeftExpression.IsNullConstantExpression();
                var isRightNullConstant = newRightExpression.IsNullConstantExpression();

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

                if (isLeftNullConstant || isRightNullConstant)
                {
                    var result = RewriteNullEquality(
                        ExpressionType.Equal, isLeftNullConstant ? newRightExpression : newLeftExpression);
                    if (result != null)
                    {
                        return(result);
                    }
                }

                var leftEntityType = _model.FindEntityType(newLeftExpression.Type)
                                     ?? MemberAccessBindingExpressionVisitor.GetEntityType(newLeftExpression, _queryCompilationContext);
                var rightEntityType = _model.FindEntityType(newRightExpression.Type)
                                      ?? MemberAccessBindingExpressionVisitor.GetEntityType(newRightExpression, _queryCompilationContext);
                if (leftEntityType != null && rightEntityType != null)
                {
                    if (leftEntityType.RootType() == rightEntityType.RootType())
                    {
                        var result = RewriteEntityEquality(ExpressionType.Equal, newLeftExpression, newRightExpression);
                        if (result != null)
                        {
                            return(result);
                        }
                    }
                    else
                    {
                        return(Expression.Constant(false));
                    }
                }
            }

            return(newMethodCallExpression);
        }
示例#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;

                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);
        }
示例#6
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);
        }