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); }
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; }
/// <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); }
/// <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); }
/// <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); }
/// <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); }