diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs index 849cbdf14e2..acfb152ade9 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs @@ -120,6 +120,29 @@ public override Expression Visit(Expression expression) { return _selectExpression.AddCollectionProjection(subquery, null, subquery.ShaperExpression.Type); } + + static bool IsAggregateResultWithCustomShaper(MethodInfo method) + { + if (method.IsGenericMethod) + { + method = method.GetGenericMethodDefinition(); + } + + return QueryableMethods.IsAverageWithoutSelector(method) + || QueryableMethods.IsAverageWithSelector(method) + || method == QueryableMethods.MaxWithoutSelector + || method == QueryableMethods.MaxWithSelector + || method == QueryableMethods.MinWithoutSelector + || method == QueryableMethods.MinWithSelector + || QueryableMethods.IsSumWithoutSelector(method) + || QueryableMethods.IsSumWithSelector(method); + } + + if (!(subquery.ShaperExpression is ProjectionBindingExpression + || IsAggregateResultWithCustomShaper(methodCallExpression.Method))) + { + return _selectExpression.AddSingleProjection(subquery); + } } break; diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs index 16f4d8c0549..8e2932d4c63 100644 --- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs @@ -762,5 +762,22 @@ protected override Expression VisitSubSelect(ScalarSubqueryExpression scalarSubq return scalarSubqueryExpression; } + + protected override Expression VisitRowNumber(RowNumberExpression rowNumberExpression) + { + _relationalCommandBuilder.Append("ROW_NUMBER() OVER("); + if (rowNumberExpression.Partitions.Any()) + { + _relationalCommandBuilder.Append("PARTITION BY "); + GenerateList(rowNumberExpression.Partitions, e => Visit(e)); + _relationalCommandBuilder.Append(" "); + } + + _relationalCommandBuilder.Append("ORDER BY "); + GenerateList(rowNumberExpression.Orderings, e => Visit(e)); + _relationalCommandBuilder.Append(")"); + + return rowNumberExpression; + } } } diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 8c057fae99e..f9fa606ad04 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -1180,43 +1180,34 @@ private ShapedQueryExpression AggregateResultShaper( Expression shaper = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), projection.Type); - if (throwOnNullResult - && resultType.IsNullableType()) + if (throwOnNullResult) { var resultVariable = Expression.Variable(projection.Type, "result"); + var returnValueForNull = resultType.IsNullableType() + ? (Expression)Expression.Constant(null, resultType) + : Expression.Throw( + Expression.New( + typeof(InvalidOperationException).GetConstructors() + .Single(ci => ci.GetParameters().Length == 1), + Expression.Constant(CoreStrings.NoElements)), + resultType); shaper = Expression.Block( new[] { resultVariable }, Expression.Assign(resultVariable, shaper), Expression.Condition( Expression.Equal(resultVariable, Expression.Default(projection.Type)), - Expression.Constant(null, resultType), + returnValueForNull, resultType != resultVariable.Type ? Expression.Convert(resultVariable, resultType) : (Expression)resultVariable)); } - else if (throwOnNullResult) - { - var resultVariable = Expression.Variable(projection.Type, "result"); - - shaper = Expression.Block( - new[] { resultVariable }, - Expression.Assign(resultVariable, shaper), - Expression.Condition( - Expression.Equal(resultVariable, Expression.Default(projection.Type)), - Expression.Throw( - Expression.New( - typeof(InvalidOperationException).GetConstructors() - .Single(ci => ci.GetParameters().Length == 1), - Expression.Constant(CoreStrings.NoElements)), - resultType), - resultType != resultVariable.Type - ? Expression.Convert(resultVariable, resultType) - : (Expression)resultVariable)); - } - else if (resultType.IsNullableType()) + else { - shaper = Expression.Convert(shaper, resultType); + if (resultType.IsNullableType()) + { + shaper = Expression.Convert(shaper, resultType); + } } source.ShaperExpression = shaper; diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index 56db829aa83..e8c136226f8 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -323,6 +323,23 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var subqueryTranslation = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression); if (subqueryTranslation != null) { + static bool IsAggregateResultWithCustomShaper(MethodInfo method) + { + if (method.IsGenericMethod) + { + method = method.GetGenericMethodDefinition(); + } + + return QueryableMethods.IsAverageWithoutSelector(method) + || QueryableMethods.IsAverageWithSelector(method) + || method == QueryableMethods.MaxWithoutSelector + || method == QueryableMethods.MaxWithSelector + || method == QueryableMethods.MinWithoutSelector + || method == QueryableMethods.MinWithSelector + || QueryableMethods.IsSumWithoutSelector(method) + || QueryableMethods.IsSumWithSelector(method); + } + if (subqueryTranslation.ResultCardinality == ResultCardinality.Enumerable) { return null; @@ -331,7 +348,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var subquery = (SelectExpression)subqueryTranslation.QueryExpression; subquery.ApplyProjection(); - if (subquery.Projection.Count != 1) + if (!(subqueryTranslation.ShaperExpression is ProjectionBindingExpression + || IsAggregateResultWithCustomShaper(methodCallExpression.Method))) { return null; } diff --git a/src/EFCore.Relational/Query/SqlExpressionVisitor.cs b/src/EFCore.Relational/Query/SqlExpressionVisitor.cs index 831dcd21530..ca2c4c2f6e5 100644 --- a/src/EFCore.Relational/Query/SqlExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/SqlExpressionVisitor.cs @@ -3,7 +3,6 @@ using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; -using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query { @@ -52,6 +51,9 @@ protected override Expression VisitExtension(Expression extensionExpression) case ProjectionExpression projectionExpression: return VisitProjection(projectionExpression); + case RowNumberExpression rowNumberExpression: + return VisitRowNumber(rowNumberExpression); + case SelectExpression selectExpression: return VisitSelect(selectExpression); @@ -83,6 +85,7 @@ protected override Expression VisitExtension(Expression extensionExpression) return base.VisitExtension(extensionExpression); } + protected abstract Expression VisitRowNumber(RowNumberExpression rowNumberExpression); protected abstract Expression VisitExists(ExistsExpression existsExpression); protected abstract Expression VisitIn(InExpression inExpression); protected abstract Expression VisitCrossJoin(CrossJoinExpression crossJoinExpression); diff --git a/src/EFCore.Relational/Query/SqlExpressions/CaseExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/CaseExpression.cs index 7abbdf61184..3653c48ac94 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/CaseExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/CaseExpression.cs @@ -122,6 +122,7 @@ private bool Equals(CaseExpression caseExpression) public override int GetHashCode() { var hash = new HashCode(); + hash.Add(base.GetHashCode()); hash.Add(Operand); for (var i = 0; i < WhenClauses.Count; i++) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/RowNumberExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/RowNumberExpression.cs new file mode 100644 index 00000000000..1314b750cae --- /dev/null +++ b/src/EFCore.Relational/Query/SqlExpressions/RowNumberExpression.cs @@ -0,0 +1,101 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions +{ + public class RowNumberExpression : SqlExpression + { + public RowNumberExpression(IReadOnlyList partitions, IReadOnlyList orderings, RelationalTypeMapping typeMapping) + : base(typeof(long), typeMapping) + { + Check.NotEmpty(orderings, nameof(orderings)); + + Partitions = partitions; + Orderings = orderings; + } + + public virtual IReadOnlyList Partitions { get; } + public virtual IReadOnlyList Orderings { get; } + + protected override Expression VisitChildren(ExpressionVisitor visitor) + { + var changed = false; + var partitions = new List(); + foreach (var partition in Partitions) + { + var newPartition = (SqlExpression)visitor.Visit(partition); + changed |= newPartition != partition; + partitions.Add(newPartition); + } + + var orderings = new List(); + foreach (var ordering in Orderings) + { + var newOrdering = (OrderingExpression)visitor.Visit(ordering); + changed |= newOrdering != ordering; + orderings.Add(newOrdering); + } + + return changed + ? new RowNumberExpression(partitions, orderings, TypeMapping) + : this; + } + + public virtual RowNumberExpression Update(IReadOnlyList partitions, IReadOnlyList orderings) + { + return (Partitions == null ? partitions == null : Partitions.SequenceEqual(partitions)) + && Orderings.SequenceEqual(orderings) + ? this + : new RowNumberExpression(partitions, orderings, TypeMapping); + } + + public override void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.Append("ROW_NUMBER() OVER("); + if (Partitions.Any()) + { + expressionPrinter.Append("PARTITION BY "); + expressionPrinter.VisitList(Partitions); + expressionPrinter.Append(" "); + } + expressionPrinter.Append("ORDER BY "); + expressionPrinter.VisitList(Orderings); + expressionPrinter.Append(")"); + } + + public override bool Equals(object obj) + => obj != null + && (ReferenceEquals(this, obj) + || obj is RowNumberExpression rowNumberExpression + && Equals(rowNumberExpression)); + + private bool Equals(RowNumberExpression rowNumberExpression) + => base.Equals(rowNumberExpression) + && (Partitions == null ? rowNumberExpression.Partitions == null : Partitions.SequenceEqual(rowNumberExpression.Partitions)) + && Orderings.SequenceEqual(rowNumberExpression.Orderings); + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + foreach (var partition in Partitions) + { + hash.Add(partition); + } + + foreach (var ordering in Orderings) + { + hash.Add(ordering); + } + + return hash.ToHashCode(); + } + } +} diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 5e145f39eac..1974429f883 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -559,6 +559,12 @@ ColumnExpression addSetOperationColumnProjections( } } + private ColumnExpression GenerateOuterColumn(SqlExpression projection, string alias = null) + { + var index = AddToProjection(projection, alias); + return new ColumnExpression(_projection[index], this); + } + public IDictionary PushdownIntoSubquery() { var subquery = new SelectExpression( @@ -574,20 +580,13 @@ public IDictionary PushdownIntoSubquery() var projectionMap = new Dictionary(); - ColumnExpression liftProjectionFromSubquery(SqlExpression projection) - { - var index = subquery.AddToProjection(projection); - var projectionExpression = subquery._projection[index]; - return new ColumnExpression(projectionExpression, subquery); - } - EntityProjectionExpression liftEntityProjectionFromSubquery(EntityProjectionExpression entityProjection) { var propertyExpressions = new Dictionary(); foreach (var property in GetAllPropertiesInHierarchy(entityProjection.EntityType)) { var innerColumn = entityProjection.BindProperty(property); - var outerColumn = liftProjectionFromSubquery(innerColumn); + var outerColumn = subquery.GenerateOuterColumn(innerColumn); projectionMap[innerColumn] = outerColumn; propertyExpressions[property] = outerColumn; } @@ -616,7 +615,7 @@ EntityProjectionExpression liftEntityProjectionFromSubquery(EntityProjectionExpr _projection.Clear(); foreach (var projection in projections) { - var outerColumn = liftProjectionFromSubquery(projection); + var outerColumn = subquery.GenerateOuterColumn(projection); AddToProjection(outerColumn); projectionMap[projection] = outerColumn; } @@ -632,7 +631,7 @@ EntityProjectionExpression liftEntityProjectionFromSubquery(EntityProjectionExpr else { var innerColumn = (SqlExpression)mapping.Value; - var outerColumn = liftProjectionFromSubquery(innerColumn); + var outerColumn = subquery.GenerateOuterColumn(innerColumn); projectionMap[innerColumn] = outerColumn; _projectionMapping[mapping.Key] = outerColumn; } @@ -650,7 +649,7 @@ EntityProjectionExpression liftEntityProjectionFromSubquery(EntityProjectionExpr } else if (!IsDistinct && GroupBy.Count == 0) { - outerColumn = liftProjectionFromSubquery(identifier); + outerColumn = subquery.GenerateOuterColumn(identifier); _identifier.Add(outerColumn); } } @@ -666,7 +665,7 @@ EntityProjectionExpression liftEntityProjectionFromSubquery(EntityProjectionExpr } else if (!IsDistinct && GroupBy.Count == 0) { - outerColumn = liftProjectionFromSubquery(identifier); + outerColumn = subquery.GenerateOuterColumn(identifier); _childIdentifiers.Add(outerColumn); } } @@ -684,7 +683,7 @@ EntityProjectionExpression liftEntityProjectionFromSubquery(EntityProjectionExpr var orderingExpression = ordering.Expression; if (!projectionMap.TryGetValue(orderingExpression, out var outerColumn)) { - outerColumn = liftProjectionFromSubquery(orderingExpression); + outerColumn = subquery.GenerateOuterColumn(orderingExpression); } _orderings.Add(ordering.Update(outerColumn)); @@ -709,6 +708,29 @@ EntityProjectionExpression liftEntityProjectionFromSubquery(EntityProjectionExpr return projectionMap; } + public Expression AddSingleProjection(ShapedQueryExpression shapedQueryExpression) + { + var innerSelectExpression = (SelectExpression)shapedQueryExpression.QueryExpression; + innerSelectExpression.ApplyProjection(); + var projectionCount = innerSelectExpression.Projection.Count; + AddLeftJoinLateral(innerSelectExpression, null); + + // Joined SelectExpression may different based on left join or outer apply + // And it will always be SelectExpression because of presence of Take(1) + // So we need to remap projections from that SelectExpression to outer SelectExpression + var addedSelectExperssion = (SelectExpression)((JoinExpressionBase)_tables[_tables.Count - 1]).Table; + var indexOffset = _projection.Count; + // We only take projectionCount since the subquery can have additional projections for identifiers + // Which are not relevant for this translation + foreach (var projection in addedSelectExperssion.Projection.Take(projectionCount)) + { + AddToProjection(MakeNullable(addedSelectExperssion.GenerateOuterColumn(projection.Expression))); + } + + return new ShaperRemappingExpressionVisitor(this, innerSelectExpression, indexOffset) + .Visit(shapedQueryExpression.ShaperExpression); + } + public CollectionShaperExpression AddCollectionProjection( ShapedQueryExpression shapedQueryExpression, INavigation navigation, Type elementType) { @@ -987,6 +1009,22 @@ public override Expression Visit(Expression expression) } } + private void GetPartitions(SqlExpression sqlExpression, List partitions) + { + if (sqlExpression is SqlBinaryExpression sqlBinaryExpression) + { + if (sqlBinaryExpression.OperatorType == ExpressionType.Equal) + { + partitions.Add(sqlBinaryExpression.Right); + } + else if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso) + { + GetPartitions(sqlBinaryExpression.Left, partitions); + GetPartitions(sqlBinaryExpression.Right, partitions); + } + } + } + private enum JoinType { InnerJoin, @@ -1005,6 +1043,10 @@ private void AddJoin( // Try to convert lateral join to normal join if (joinType == JoinType.InnerJoinLateral || joinType == JoinType.LeftJoinLateral) { + // Doing for limit only since limit + offset may need sum + var limit = innerSelectExpression.Limit; + innerSelectExpression.Limit = null; + joinPredicate = TryExtractJoinKey(innerSelectExpression); if (joinPredicate != null) { @@ -1013,21 +1055,50 @@ private void AddJoin( if (containsOuterReference) { innerSelectExpression.ApplyPredicate(joinPredicate); + innerSelectExpression.ApplyLimit(limit); } else { + if (limit != null) + { + var partitions = new List(); + GetPartitions(joinPredicate, partitions); + var orderings = innerSelectExpression.Orderings.Any() + ? innerSelectExpression.Orderings + : innerSelectExpression._identifier.Select(e => new OrderingExpression(e, true)); + var rowNumberExpression = new RowNumberExpression(partitions, orderings.ToList(), limit.TypeMapping); + innerSelectExpression.ClearOrdering(); + + var projectionMappings = innerSelectExpression.PushdownIntoSubquery(); + var subquery = (SelectExpression)innerSelectExpression.Tables[0]; + + joinPredicate = new SqlRemappingVisitor( + projectionMappings, subquery) + .Remap(joinPredicate); + + var outerColumn = subquery.GenerateOuterColumn(rowNumberExpression, "row"); + var predicate = new SqlBinaryExpression( + ExpressionType.LessThanOrEqual, outerColumn, limit, typeof(bool), joinPredicate.TypeMapping); + innerSelectExpression.ApplyPredicate(predicate); + } + AddJoin(joinType == JoinType.InnerJoinLateral ? JoinType.InnerJoin : JoinType.LeftJoin, innerSelectExpression, transparentIdentifierType, joinPredicate); return; } } + else + { + innerSelectExpression.ApplyLimit(limit); + } } // Verify what are the cases of pushdown for inner & outer both sides if (Limit != null || Offset != null || IsDistinct || IsSetOperation || GroupBy.Count > 1) { - joinPredicate = new SqlRemappingVisitor(PushdownIntoSubquery(), (SelectExpression)Tables[0]) - .Remap(joinPredicate); + var sqlRemappingVisitor = new SqlRemappingVisitor(PushdownIntoSubquery(), (SelectExpression)Tables[0]); + innerSelectExpression = sqlRemappingVisitor.Remap(innerSelectExpression); + joinPredicate = sqlRemappingVisitor.Remap(joinPredicate); } if (innerSelectExpression.Orderings.Any() diff --git a/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs index 2a88ebbc89f..488045538ab 100644 --- a/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs @@ -361,5 +361,31 @@ protected override Expression VisitSubSelect(ScalarSubqueryExpression scalarSubq return ApplyConversion(scalarSubqueryExpression.Update(subquery), condition: false); } + + protected override Expression VisitRowNumber(RowNumberExpression rowNumberExpression) + { + var parentSearchCondition = _isSearchCondition; + _isSearchCondition = false; + var changed = false; + var partitions = new List(); + foreach (var partition in rowNumberExpression.Partitions) + { + var newPartition = (SqlExpression)Visit(partition); + changed |= newPartition != partition; + partitions.Add(newPartition); + } + + var orderings = new List(); + foreach (var ordering in rowNumberExpression.Orderings) + { + var newOrdering = (OrderingExpression)Visit(ordering); + changed |= newOrdering != ordering; + orderings.Add(newOrdering); + } + + _isSearchCondition = parentSearchCondition; + + return ApplyConversion(rowNumberExpression.Update(partitions, orderings), condition: false); + } } } diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.Select.cs b/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.Select.cs index ab1017e7680..5a4057684c1 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.Select.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.Select.cs @@ -672,6 +672,7 @@ FROM root c WHERE ((c[""Discriminator""] = ""Order"") AND (c[""OrderID""] < 10250))"); } + [ConditionalTheory(Skip = "Issue #17246")] public override async Task Project_single_element_from_collection_with_OrderBy_over_navigation_Take_and_FirstOrDefault_2( bool isAsync) { diff --git a/test/EFCore.InMemory.FunctionalTests/Query/QueryNavigationsInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/QueryNavigationsInMemoryTest.cs index e06585cdecc..35b19881983 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/QueryNavigationsInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/QueryNavigationsInMemoryTest.cs @@ -51,5 +51,47 @@ public override void Collection_where_nav_prop_all_client() [ConditionalTheory(Skip = "Issue #16963")] public override Task Select_collection_navigation_multi_part2(bool isAsync) => null; + + [ConditionalTheory(Skip = "Issue #16963")] + public override Task Collection_select_nav_prop_first_or_default(bool isAsync) + { + return base.Collection_select_nav_prop_first_or_default(isAsync); + } + + [ConditionalTheory(Skip = "Issue #16963")] + public override Task Collection_select_nav_prop_first_or_default_then_nav_prop(bool isAsync) + { + return base.Collection_select_nav_prop_first_or_default_then_nav_prop(isAsync); + } + + [ConditionalTheory(Skip = "Issue #16963")] + public override Task Project_single_entity_value_subquery_works(bool isAsync) + { + return base.Project_single_entity_value_subquery_works(isAsync); + } + + [ConditionalTheory(Skip = "Issue #16963")] + public override Task Select_collection_FirstOrDefault_project_anonymous_type(bool isAsync) + { + return base.Select_collection_FirstOrDefault_project_anonymous_type(isAsync); + } + + [ConditionalTheory(Skip = "Issue #16963")] + public override Task Select_collection_FirstOrDefault_project_entity(bool isAsync) + { + return base.Select_collection_FirstOrDefault_project_entity(isAsync); + } + + [ConditionalTheory(Skip = "Issue #16963")] + public override Task Skip_Select_Navigation(bool isAsync) + { + return base.Skip_Select_Navigation(isAsync); + } + + [ConditionalTheory(Skip = "Issue #16963")] + public override Task Take_Select_Navigation(bool isAsync) + { + return base.Take_Select_Navigation(isAsync); + } } } diff --git a/test/EFCore.InMemory.FunctionalTests/Query/SimpleQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/SimpleQueryInMemoryTest.cs index f04122e35eb..9f3c9875f83 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/SimpleQueryInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/SimpleQueryInMemoryTest.cs @@ -423,5 +423,11 @@ public override Task Last_when_no_order_by(bool isAsync) { return base.Last_when_no_order_by(isAsync); } + + [ConditionalTheory(Skip = "Issue #16963")] + public override Task Project_single_element_from_collection_with_OrderBy_over_navigation_Take_and_FirstOrDefault_2(bool isAsync) + { + return base.Project_single_element_from_collection_with_OrderBy_over_navigation_Take_and_FirstOrDefault_2(isAsync); + } } } diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index 6344e3c113a..678e8fe27cc 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -6324,7 +6324,7 @@ public virtual Task Select_subquery_projecting_single_constant_bool(bool isAsync })); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory(Skip = "Issue#17287")] [MemberData(nameof(IsAsyncData))] public virtual Task Select_subquery_projecting_single_constant_inside_anonymous(bool isAsync) { @@ -6342,7 +6342,7 @@ public virtual Task Select_subquery_projecting_single_constant_inside_anonymous( })); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory(Skip = "Issue#17287")] [MemberData(nameof(IsAsyncData))] public virtual Task Select_subquery_projecting_multiple_constants_inside_anonymous(bool isAsync) { diff --git a/test/EFCore.Specification.Tests/Query/QueryNavigationsTestBase.cs b/test/EFCore.Specification.Tests/Query/QueryNavigationsTestBase.cs index e19dedff3ce..aa267139027 100644 --- a/test/EFCore.Specification.Tests/Query/QueryNavigationsTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/QueryNavigationsTestBase.cs @@ -250,14 +250,15 @@ public virtual Task Select_Where_Navigation_Deep(bool isAsync) entryCount: 1); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Take_Select_Navigation(bool isAsync) { return AssertQuery( isAsync, cs => cs.OrderBy(c => c.CustomerID).Take(2) - .Select(c => c.Orders.OrderBy(o => o.OrderID).FirstOrDefault())); + .Select(c => c.Orders.OrderBy(o => o.OrderID).FirstOrDefault()), + entryCount: 2); } [ConditionalTheory] @@ -279,7 +280,7 @@ public virtual Task Select_collection_FirstOrDefault_project_single_column2(bool .Select(c => c.Orders.OrderBy(o => o.OrderID).Select(o => o.CustomerID).FirstOrDefault())); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Select_collection_FirstOrDefault_project_anonymous_type(bool isAsync) { @@ -295,16 +296,17 @@ public virtual Task Select_collection_FirstOrDefault_project_anonymous_type(bool assertOrder: true); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Select_collection_FirstOrDefault_project_entity(bool isAsync) { return AssertQuery( isAsync, - cs => cs.OrderBy(c => c.CustomerID).Take(2).Select(c => c.Orders.OrderBy(o => o.OrderID).FirstOrDefault())); + cs => cs.OrderBy(c => c.CustomerID).Take(2).Select(c => c.Orders.OrderBy(o => o.OrderID).FirstOrDefault()), + entryCount: 2); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Skip_Select_Navigation(bool isAsync) { @@ -313,6 +315,7 @@ public virtual Task Skip_Select_Navigation(bool isAsync) cs => cs.OrderBy(c => c.CustomerID) .Skip(20) .Select(c => c.Orders.OrderBy(o => o.OrderID).FirstOrDefault()), + entryCount: 69, assertOrder: true); } @@ -962,7 +965,7 @@ where c.Orders.Sum(o => o.OrderID) > 1000 } } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Collection_select_nav_prop_first_or_default(bool isAsync) { @@ -980,10 +983,11 @@ orderby c.CustomerID { First = (c.Orders ?? new List()).FirstOrDefault() }, - assertOrder: true); + assertOrder: true, + entryCount: 89); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Collection_select_nav_prop_first_or_default_then_nav_prop(bool isAsync) { @@ -1008,7 +1012,8 @@ orderby c.CustomerID : null }, elementSorter: e => e.Customer?.CustomerID, - elementAsserter: (e, a) => Assert.Equal(e.Customer?.CustomerID, a.Customer?.CustomerID)); + elementAsserter: (e, a) => Assert.Equal(e.Customer?.CustomerID, a.Customer?.CustomerID), + entryCount: 1); } [ConditionalTheory] @@ -1316,7 +1321,7 @@ public virtual Task Project_single_scalar_value_subquery_is_properly_inlined(boo elementSorter: e => e.CustomerID); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Project_single_entity_value_subquery_works(bool isAsync) { @@ -1330,7 +1335,8 @@ orderby c.CustomerID c.CustomerID, Order = c.Orders.OrderBy(o => o.OrderID).FirstOrDefault() }, - elementSorter: e => e.CustomerID); + elementSorter: e => e.CustomerID, + entryCount: 4); } [ConditionalTheory] diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Select.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Select.cs index ec69ded6639..0ae165e37ea 100644 --- a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Select.cs +++ b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Select.cs @@ -940,7 +940,7 @@ public virtual Task Project_single_element_from_collection_with_OrderBy_over_nav o => o.OrderDetails.OrderBy(od => od.Product.ProductName).Select(od => od.OrderID).Take(1).FirstOrDefault())); } - [ConditionalTheory(Skip = "Issue#10001")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Project_single_element_from_collection_with_OrderBy_over_navigation_Take_and_FirstOrDefault_2(bool isAsync) { @@ -948,7 +948,8 @@ public virtual Task Project_single_element_from_collection_with_OrderBy_over_nav isAsync, os => os.Where(o => o.OrderID < 10250) .Select( - o => o.OrderDetails.OrderBy(od => od.Product.ProductName).Take(1).FirstOrDefault())); + o => o.OrderDetails.OrderBy(od => od.Product.ProductName).Take(1).FirstOrDefault()), + entryCount: 2); } [ConditionalTheory] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryNavigationsSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryNavigationsSqlServerTest.cs index 77d1af30710..eab35b87cc8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryNavigationsSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryNavigationsSqlServerTest.cs @@ -111,23 +111,21 @@ public override async Task Take_Select_Navigation(bool isAsync) AssertSql( @"@__p_0='2' -SELECT TOP(@__p_0) [c].[CustomerID] -FROM [Customers] AS [c] -ORDER BY [c].[CustomerID]", - // - @"@_outer_CustomerID='ALFKI' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]", - // - @"@_outer_CustomerID='ANATR' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]"); +SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY [c].[CustomerID] +) AS [t1] +LEFT JOIN ( + SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] + FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + FROM [Orders] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [t1].[CustomerID] = [t0].[CustomerID] +ORDER BY [t1].[CustomerID]"); } public override async Task Select_collection_FirstOrDefault_project_single_column1(bool isAsync) @@ -169,23 +167,21 @@ public override async Task Select_collection_FirstOrDefault_project_anonymous_ty AssertSql( @"@__p_0='2' -SELECT TOP(@__p_0) [c].[CustomerID] -FROM [Customers] AS [c] -ORDER BY [c].[CustomerID]", - // - @"@_outer_CustomerID='ALFKI' (Size = 5) - -SELECT TOP(1) [o].[CustomerID], [o].[OrderID] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]", - // - @"@_outer_CustomerID='ANATR' (Size = 5) - -SELECT TOP(1) [o].[CustomerID], [o].[OrderID] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]"); +SELECT [t0].[CustomerID], [t0].[OrderID] +FROM ( + SELECT TOP(@__p_0) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY [c].[CustomerID] +) AS [t1] +LEFT JOIN ( + SELECT [t].[CustomerID], [t].[OrderID] + FROM ( + SELECT [o].[CustomerID], [o].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + FROM [Orders] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [t1].[CustomerID] = [t0].[CustomerID] +ORDER BY [t1].[CustomerID]"); } public override async Task Select_collection_FirstOrDefault_project_entity(bool isAsync) @@ -195,23 +191,21 @@ public override async Task Select_collection_FirstOrDefault_project_entity(bool AssertSql( @"@__p_0='2' -SELECT TOP(@__p_0) [c].[CustomerID] -FROM [Customers] AS [c] -ORDER BY [c].[CustomerID]", - // - @"@_outer_CustomerID='ALFKI' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]", - // - @"@_outer_CustomerID='ANATR' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]"); +SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] +FROM ( + SELECT TOP(@__p_0) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY [c].[CustomerID] +) AS [t1] +LEFT JOIN ( + SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] + FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + FROM [Orders] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [t1].[CustomerID] = [t0].[CustomerID] +ORDER BY [t1].[CustomerID]"); } public override async Task Skip_Select_Navigation(bool isAsync) @@ -223,24 +217,22 @@ public override async Task Skip_Select_Navigation(bool isAsync) AssertSql( @"@__p_0='20' -SELECT [c].[CustomerID] -FROM [Customers] AS [c] -ORDER BY [c].[CustomerID] -OFFSET @__p_0 ROWS", - // - @"@_outer_CustomerID='FAMIA' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]", - // - @"@_outer_CustomerID='FISSA' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]"); +SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY [c].[CustomerID] + OFFSET @__p_0 ROWS +) AS [t1] +LEFT JOIN ( + SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] + FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + FROM [Orders] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [t1].[CustomerID] = [t0].[CustomerID] +ORDER BY [t1].[CustomerID]"); } } @@ -691,23 +683,17 @@ public override async Task Collection_select_nav_prop_first_or_default(bool isAs await base.Collection_select_nav_prop_first_or_default(isAsync); AssertSql( - @"SELECT [c].[CustomerID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] FROM [Customers] AS [c] -ORDER BY [c].[CustomerID]", - // - @"@_outer_CustomerID='ALFKI' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]", - // - @"@_outer_CustomerID='ANATR' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]"); +LEFT JOIN ( + SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] + FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + FROM [Orders] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] +ORDER BY [c].[CustomerID]"); } public override async Task Collection_select_nav_prop_first_or_default_then_nav_prop(bool isAsync) @@ -715,38 +701,20 @@ public override async Task Collection_select_nav_prop_first_or_default_then_nav_ await base.Collection_select_nav_prop_first_or_default_then_nav_prop(isAsync); AssertSql( - @"SELECT [e].[CustomerID] -FROM [Customers] AS [e] -WHERE [e].[CustomerID] LIKE N'A%' -ORDER BY [e].[CustomerID]", - // - @"@_outer_CustomerID='ALFKI' (Size = 5) - -SELECT TOP(1) [e.Customer].[CustomerID], [e.Customer].[Address], [e.Customer].[City], [e.Customer].[CompanyName], [e.Customer].[ContactName], [e.Customer].[ContactTitle], [e.Customer].[Country], [e.Customer].[Fax], [e.Customer].[Phone], [e.Customer].[PostalCode], [e.Customer].[Region] -FROM [Orders] AS [e0] -LEFT JOIN [Customers] AS [e.Customer] ON [e0].[CustomerID] = [e.Customer].[CustomerID] -WHERE [e0].[OrderID] IN (10643, 10692, 10702, 10835, 10952, 11011) AND (@_outer_CustomerID = [e0].[CustomerID])", - // - @"@_outer_CustomerID='ANATR' (Size = 5) - -SELECT TOP(1) [e.Customer].[CustomerID], [e.Customer].[Address], [e.Customer].[City], [e.Customer].[CompanyName], [e.Customer].[ContactName], [e.Customer].[ContactTitle], [e.Customer].[Country], [e.Customer].[Fax], [e.Customer].[Phone], [e.Customer].[PostalCode], [e.Customer].[Region] -FROM [Orders] AS [e0] -LEFT JOIN [Customers] AS [e.Customer] ON [e0].[CustomerID] = [e.Customer].[CustomerID] -WHERE [e0].[OrderID] IN (10643, 10692, 10702, 10835, 10952, 11011) AND (@_outer_CustomerID = [e0].[CustomerID])", - // - @"@_outer_CustomerID='ANTON' (Size = 5) - -SELECT TOP(1) [e.Customer].[CustomerID], [e.Customer].[Address], [e.Customer].[City], [e.Customer].[CompanyName], [e.Customer].[ContactName], [e.Customer].[ContactTitle], [e.Customer].[Country], [e.Customer].[Fax], [e.Customer].[Phone], [e.Customer].[PostalCode], [e.Customer].[Region] -FROM [Orders] AS [e0] -LEFT JOIN [Customers] AS [e.Customer] ON [e0].[CustomerID] = [e.Customer].[CustomerID] -WHERE [e0].[OrderID] IN (10643, 10692, 10702, 10835, 10952, 11011) AND (@_outer_CustomerID = [e0].[CustomerID])", - // - @"@_outer_CustomerID='AROUT' (Size = 5) - -SELECT TOP(1) [e.Customer].[CustomerID], [e.Customer].[Address], [e.Customer].[City], [e.Customer].[CompanyName], [e.Customer].[ContactName], [e.Customer].[ContactTitle], [e.Customer].[Country], [e.Customer].[Fax], [e.Customer].[Phone], [e.Customer].[PostalCode], [e.Customer].[Region] -FROM [Orders] AS [e0] -LEFT JOIN [Customers] AS [e.Customer] ON [e0].[CustomerID] = [e.Customer].[CustomerID] -WHERE [e0].[OrderID] IN (10643, 10692, 10702, 10835, 10952, 11011) AND (@_outer_CustomerID = [e0].[CustomerID])"); + @"SELECT [t0].[CustomerID], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM [Customers] AS [c0] +LEFT JOIN ( + SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region], [t].[OrderID], [t].[CustomerID0] + FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID] AS [CustomerID0], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + FROM [Orders] AS [o] + LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] + WHERE [o].[OrderID] IN (10643, 10692, 10702, 10835, 10952, 11011) + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [c0].[CustomerID] = [t0].[CustomerID0] +WHERE [c0].[CustomerID] LIKE N'A%' +ORDER BY [c0].[CustomerID]"); } public override async Task Collection_select_nav_prop_first_or_default_then_nav_prop_nested(bool isAsync) @@ -971,38 +939,18 @@ public override async Task Project_single_entity_value_subquery_works(bool isAsy await base.Project_single_entity_value_subquery_works(isAsync); AssertSql( - @"SELECT [c].[CustomerID] + @"SELECT [c].[CustomerID], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] FROM [Customers] AS [c] +LEFT JOIN ( + SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] + FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + FROM [Orders] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] WHERE [c].[CustomerID] LIKE N'A%' -ORDER BY [c].[CustomerID]", - // - @"@_outer_CustomerID='ALFKI' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]", - // - @"@_outer_CustomerID='ANATR' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]", - // - @"@_outer_CustomerID='ANTON' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]", - // - @"@_outer_CustomerID='AROUT' (Size = 5) - -SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -WHERE @_outer_CustomerID = [o].[CustomerID] -ORDER BY [o].[OrderID]"); +ORDER BY [c].[CustomerID]"); } public override async Task Project_single_scalar_value_subquery_in_query_with_optional_navigation_works(bool isAsync) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Select.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Select.cs index cf9858d1ce8..deec85df11c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Select.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Select.cs @@ -723,33 +723,20 @@ public override async Task Project_single_element_from_collection_with_OrderBy_o await base.Project_single_element_from_collection_with_OrderBy_over_navigation_Take_and_FirstOrDefault_2(isAsync); AssertSql( - @"SELECT [o].[OrderID] -FROM [Orders] AS [o] -WHERE [o].[OrderID] < 10250", - // - @"@_outer_OrderID='10248' - -SELECT TOP(1) [t].* -FROM ( - SELECT TOP(1) [od].[OrderID], [od].[ProductID], [od].[Discount], [od].[Quantity], [od].[UnitPrice], [od.Product].[ProductName] - FROM [Order Details] AS [od] - INNER JOIN [Products] AS [od.Product] ON [od].[ProductID] = [od.Product].[ProductID] - WHERE @_outer_OrderID = [od].[OrderID] - ORDER BY [od.Product].[ProductName] -) AS [t] -ORDER BY [t].[ProductName]", - // - @"@_outer_OrderID='10249' - -SELECT TOP(1) [t].* -FROM ( - SELECT TOP(1) [od].[OrderID], [od].[ProductID], [od].[Discount], [od].[Quantity], [od].[UnitPrice], [od.Product].[ProductName] - FROM [Order Details] AS [od] - INNER JOIN [Products] AS [od.Product] ON [od].[ProductID] = [od.Product].[ProductID] - WHERE @_outer_OrderID = [od].[OrderID] - ORDER BY [od.Product].[ProductName] -) AS [t] -ORDER BY [t].[ProductName]"); + @"SELECT [t0].[OrderID], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice] +FROM [Orders] AS [o0] +OUTER APPLY ( + SELECT TOP(1) [t].[OrderID], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice], [t].[ProductID0], [t].[ProductName] + FROM ( + SELECT TOP(1) [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice], [p].[ProductID] AS [ProductID0], [p].[ProductName] + FROM [Order Details] AS [o] + INNER JOIN [Products] AS [p] ON [o].[ProductID] = [p].[ProductID] + WHERE [o0].[OrderID] = [o].[OrderID] + ORDER BY [p].[ProductName] + ) AS [t] + ORDER BY [t].[ProductName] +) AS [t0] +WHERE [o0].[OrderID] < 10250"); } public override async Task Select_datetime_year_component(bool isAsync) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs index 181001f4e9f..40f7c30f14b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs @@ -1922,13 +1922,16 @@ public override async Task SelectMany_Joined_Take(bool isAsync) await base.SelectMany_Joined_Take(isAsync); AssertSql( - @"SELECT [c].[ContactName], [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] + @"SELECT [c].[ContactName], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] FROM [Customers] AS [c] -CROSS APPLY ( - SELECT TOP(4) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] - FROM [Orders] AS [o] - WHERE ([o].[CustomerID] = [c].[CustomerID]) AND [o].[CustomerID] IS NOT NULL -) AS [t]"); +INNER JOIN ( + SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] + FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + FROM [Orders] AS [o] + ) AS [t] + WHERE [t].[row] <= 4 +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID]"); } public override async Task Take_with_single(bool isAsync) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs index 916bc421bed..0b55c29186d 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs @@ -1322,6 +1322,13 @@ public override void Select_nested_collection_multi_level() public override Task SelectMany_correlated_with_outer_4(bool isAsync) => null; + + [ConditionalTheory(Skip = "Issue#17324")] + public override Task Project_single_element_from_collection_with_OrderBy_over_navigation_Take_and_FirstOrDefault_2(bool isAsync) + { + return base.Project_single_element_from_collection_with_OrderBy_over_navigation_Take_and_FirstOrDefault_2(isAsync); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); }