diff --git a/src/EFCore/Query/EntityQueryModelVisitor.cs b/src/EFCore/Query/EntityQueryModelVisitor.cs index 6ab4064f91b..593251f1f69 100644 --- a/src/EFCore/Query/EntityQueryModelVisitor.cs +++ b/src/EFCore/Query/EntityQueryModelVisitor.cs @@ -300,6 +300,9 @@ protected virtual void OptimizeQueryModel( _queryOptimizer.Optimize(QueryCompilationContext.QueryAnnotations, queryModel); + new MainFromClauseFlatteningQueryModelVisitor(QueryCompilationContext.QueryAnnotations) + .VisitQueryModel(queryModel); + var entityEqualityRewritingExpressionVisitor = new EntityEqualityRewritingExpressionVisitor(QueryCompilationContext.Model); diff --git a/src/EFCore/Query/ExpressionVisitors/Internal/MainFromClauseFlatteningQueryModelVisitor.cs b/src/EFCore/Query/ExpressionVisitors/Internal/MainFromClauseFlatteningQueryModelVisitor.cs new file mode 100644 index 00000000000..3c64b26edd6 --- /dev/null +++ b/src/EFCore/Query/ExpressionVisitors/Internal/MainFromClauseFlatteningQueryModelVisitor.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.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Query.Internal; +using Microsoft.EntityFrameworkCore.Query.ResultOperators; +using Remotion.Linq; +using Remotion.Linq.Clauses; +using Remotion.Linq.Clauses.Expressions; +using Remotion.Linq.Clauses.ResultOperators; +using Remotion.Linq.Parsing; + +namespace Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal +{ + /// + /// 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. + /// + public class MainFromClauseFlatteningQueryModelVisitor : QueryModelVisitorBase + { + private readonly IEnumerable _queryAnnotations; + private readonly SubQueryExpressionVisitor _subQueryExpressionVisitor; + + /// + /// 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. + /// + public MainFromClauseFlatteningQueryModelVisitor([NotNull] IEnumerable queryAnnotations) + { + _queryAnnotations = queryAnnotations; + _subQueryExpressionVisitor = new SubQueryExpressionVisitor(this); + } + + /// + /// 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. + /// + public override void VisitQueryModel([NotNull] QueryModel queryModel) + { + queryModel.TransformExpressions(_subQueryExpressionVisitor.Visit); + + base.VisitQueryModel(queryModel); + } + + /// + /// 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. + /// + public override void VisitMainFromClause([NotNull] MainFromClause fromClause, [NotNull] QueryModel queryModel) + { + if (fromClause.FromExpression is SubQueryExpression subQueryExpression) + { + var subQueryModel = subQueryExpression.QueryModel; + + if (subQueryModel.SelectClause.Selector is QuerySourceReferenceExpression + && !subQueryModel.ResultOperators.OfType().Any() + && !queryModel.BodyClauses.Any()) + { + queryModel.UpdateQuerySourceMapping( + queryModel.MainFromClause, + subQueryModel.SelectClause.Selector, + _queryAnnotations); + + queryModel.MainFromClause = subQueryModel.MainFromClause; + + foreach (var bodyClause in subQueryModel.BodyClauses) + { + queryModel.BodyClauses.Add(bodyClause); + } + + foreach (var resultOperator in subQueryModel.ResultOperators.Reverse()) + { + queryModel.ResultOperators.Insert(0, resultOperator); + } + + return; + } + } + } + + private class SubQueryExpressionVisitor : RelinqExpressionVisitor + { + private readonly MainFromClauseFlatteningQueryModelVisitor _queryModelVisitor; + + public SubQueryExpressionVisitor(MainFromClauseFlatteningQueryModelVisitor queryModelVisitor) + { + _queryModelVisitor = queryModelVisitor; + } + + protected override Expression VisitSubQuery(SubQueryExpression expression) + { + _queryModelVisitor.VisitQueryModel(expression.QueryModel); + + return expression; + } + } + } +} diff --git a/src/EFCore/Query/Internal/QueryModelExtensions.cs b/src/EFCore/Query/Internal/QueryModelExtensions.cs index e990f14c3b9..dd184eef518 100644 --- a/src/EFCore/Query/Internal/QueryModelExtensions.cs +++ b/src/EFCore/Query/Internal/QueryModelExtensions.cs @@ -5,9 +5,11 @@ using System.Linq.Expressions; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors; +using Microsoft.EntityFrameworkCore.Query.ResultOperators; using Remotion.Linq; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; +using Remotion.Linq.Clauses.ExpressionVisitors; namespace Microsoft.EntityFrameworkCore.Query.Internal { @@ -24,6 +26,52 @@ public static class QueryModelExtensions public static string Print([NotNull] this QueryModel queryModel, bool removeFormatting = false, int? characterLimit = null) => new QueryModelPrinter().Print(queryModel, removeFormatting, characterLimit); + /// + /// 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. + /// + public static void UpdateQuerySourceMapping( + [NotNull] this QueryModel queryModel, + [NotNull] IQuerySource oldQuerySource, + [NotNull] Expression newExpression, + [NotNull] IEnumerable queryAnnotations) + { + var querySourceMapping = new QuerySourceMapping(); + querySourceMapping.AddMapping(oldQuerySource, newExpression); + + queryModel.TransformExpressions(expression => + ReferenceReplacingExpressionVisitor.ReplaceClauseReferences( + expression, + querySourceMapping, + throwOnUnmappedReferences: false)); + + if (newExpression is QuerySourceReferenceExpression qsre) + { + foreach (var queryAnnotation in queryAnnotations) + { + if (queryAnnotation.QuerySource == oldQuerySource) + { + queryAnnotation.QuerySource = qsre.ReferencedQuerySource; + queryAnnotation.QueryModel = queryModel; + } + } + } + } + + /// + /// 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. + /// + public static void UpdateQuerySourceMapping( + [NotNull] this QueryModel queryModel, + [NotNull] IQuerySource oldQuerySource, + [NotNull] IQuerySource newQuerySource, + [NotNull] IEnumerable queryAnnotations) + => queryModel.UpdateQuerySourceMapping( + oldQuerySource, + new QuerySourceReferenceExpression(newQuerySource), + queryAnnotations); + /// /// 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. diff --git a/src/EFCore/Query/Internal/QueryOptimizer.cs b/src/EFCore/Query/Internal/QueryOptimizer.cs index 7b3b077c888..b8d7614629d 100644 --- a/src/EFCore/Query/Internal/QueryOptimizer.cs +++ b/src/EFCore/Query/Internal/QueryOptimizer.cs @@ -13,7 +13,6 @@ using Remotion.Linq; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; -using Remotion.Linq.Clauses.ExpressionVisitors; using Remotion.Linq.Clauses.ResultOperators; using Remotion.Linq.Transformations; @@ -102,9 +101,7 @@ public override void VisitJoinClause(JoinClause joinClause, QueryModel queryMode private void TryFlattenJoin(JoinClause joinClause, QueryModel queryModel) { - var subQueryExpression = joinClause.InnerSequence as SubQueryExpression; - - if (subQueryExpression != null) + if (joinClause.InnerSequence is SubQueryExpression subQueryExpression) { VisitQueryModel(subQueryExpression.QueryModel); @@ -135,29 +132,21 @@ public override void VisitGroupJoinClause(GroupJoinClause groupJoinClause, Query // Attempts to rewrite GroupJoin/SelectMany to regular join - var additionalFromClause - = queryModel.BodyClauses.ElementAtOrDefault(index + 1) - as AdditionalFromClause; - - var querySourceReferenceExpression - = additionalFromClause?.FromExpression as QuerySourceReferenceExpression; - - if (querySourceReferenceExpression != null - && querySourceReferenceExpression.ReferencedQuerySource == groupJoinClause) + if (queryModel.BodyClauses.ElementAtOrDefault(index + 1) is AdditionalFromClause additionalFromClause + && additionalFromClause.FromExpression is QuerySourceReferenceExpression qsre + && qsre.ReferencedQuerySource == groupJoinClause + && queryModel.CountQuerySourceReferences(groupJoinClause) == 1) { - if (queryModel.CountQuerySourceReferences(groupJoinClause) == 1) - { - // GroupJoin/SelectMany can be rewritten to regular Join. + // GroupJoin/SelectMany can be rewritten to regular Join. - queryModel.BodyClauses.RemoveAt(index + 1); - queryModel.BodyClauses.RemoveAt(index); - queryModel.BodyClauses.Insert(index, groupJoinClause.JoinClause); + queryModel.BodyClauses.RemoveAt(index + 1); + queryModel.BodyClauses.RemoveAt(index); + queryModel.BodyClauses.Insert(index, groupJoinClause.JoinClause); - UpdateQuerySourceMapping( - queryModel, - additionalFromClause, - new QuerySourceReferenceExpression(groupJoinClause.JoinClause)); - } + queryModel.UpdateQuerySourceMapping( + additionalFromClause, + groupJoinClause.JoinClause, + _queryAnnotations); } } @@ -175,11 +164,10 @@ protected override void FlattenSubQuery( VisitQueryModel(subQueryModel); - if (subQueryModel.ResultOperators - .All(ro => ro is CastResultOperator) - && !subQueryModel.BodyClauses.Any(bc => bc is OrderByClause) - || queryModel.IsIdentityQuery() - && !queryModel.ResultOperators.Any()) + if ((subQueryModel.ResultOperators.All(ro => ro is CastResultOperator) + && !subQueryModel.BodyClauses.Any(bc => bc is OrderByClause)) + || (queryModel.IsIdentityQuery() + && !queryModel.ResultOperators.Any())) { string itemName; @@ -202,10 +190,10 @@ var fromClauseData fromClause.CopyFromSource(fromClauseData); - UpdateQuerySourceMapping( - queryModel, + queryModel.UpdateQuerySourceMapping( fromClause, - subQueryExpression.QueryModel.SelectClause.Selector); + subQueryExpression.QueryModel.SelectClause.Selector, + _queryAnnotations); InsertBodyClauses(subQueryExpression.QueryModel.BodyClauses, queryModel, destinationIndex); @@ -214,10 +202,10 @@ var fromClauseData queryModel.ResultOperators.Insert(0, resultOperator); } - UpdateQuerySourceMapping( - queryModel, + queryModel.UpdateQuerySourceMapping( innerMainFromClause, - new QuerySourceReferenceExpression(fromClause)); + new QuerySourceReferenceExpression(fromClause), + _queryAnnotations); } } @@ -240,25 +228,25 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer } } } - - var ofTypeOperator = resultOperator as OfTypeResultOperator; - if (ofTypeOperator != null) + + if (resultOperator is OfTypeResultOperator ofTypeOperator) { - var searchedItemType = ofTypeOperator.SearchedItemType; - if (searchedItemType == queryModel.MainFromClause.ItemType) + if (ofTypeOperator.SearchedItemType == queryModel.MainFromClause.ItemType) { queryModel.ResultOperators.RemoveAt(index); } else { - var entityType = _model.FindEntityType(searchedItemType); + var entityType = _model.FindEntityType(ofTypeOperator.SearchedItemType); if (entityType != null) { var oldQuerySource = queryModel.MainFromClause; var entityQueryProvider - = ((oldQuerySource.FromExpression as ConstantExpression)?.Value as IQueryable)?.Provider as IAsyncQueryProvider; + = ((oldQuerySource.FromExpression as ConstantExpression) + ?.Value as IQueryable) + ?.Provider as IAsyncQueryProvider; if (entityQueryProvider != null) { @@ -272,9 +260,10 @@ var newMainFromClause queryModel.MainFromClause = newMainFromClause; - UpdateQuerySourceMapping(queryModel, + queryModel.UpdateQuerySourceMapping( oldQuerySource, - new QuerySourceReferenceExpression(newMainFromClause)); + newMainFromClause, + _queryAnnotations); } } } @@ -282,29 +271,5 @@ var newMainFromClause base.VisitResultOperator(resultOperator, queryModel, index); } - - private void UpdateQuerySourceMapping( - QueryModel queryModel, - IQuerySource oldQuerySource, - Expression newExpression) - { - var querySourceMapping = new QuerySourceMapping(); - querySourceMapping.AddMapping(oldQuerySource, newExpression); - - queryModel.TransformExpressions(e => - ReferenceReplacingExpressionVisitor - .ReplaceClauseReferences(e, querySourceMapping, throwOnUnmappedReferences: false)); - - var qsre = newExpression as QuerySourceReferenceExpression; - if (qsre != null) - { - var newQuerySource = qsre.ReferencedQuerySource; - foreach (var queryAnnotation in _queryAnnotations.Where(qa => qa.QuerySource == oldQuerySource)) - { - queryAnnotation.QuerySource = newQuerySource; - queryAnnotation.QueryModel = queryModel; - } - } - } } } diff --git a/test/EFCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs index 710694be423..8eb184f4d05 100644 --- a/test/EFCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs @@ -1868,13 +1868,10 @@ public override void Projection_select_correct_table_from_subquery_when_material Assert.Equal( @"@__p_0: 3 -SELECT [t].[Name] -FROM ( - SELECT TOP(@__p_0) [l2].* - FROM [Level2] AS [l2] - INNER JOIN [Level1] AS [l2.OneToOne_Required_FK_Inverse] ON [l2].[Level1_Required_Id] = [l2.OneToOne_Required_FK_Inverse].[Id] - WHERE [l2.OneToOne_Required_FK_Inverse].[Name] = N'L1 03' -) AS [t]", +SELECT TOP(@__p_0) [l2].[Name] +FROM [Level2] AS [l2] +INNER JOIN [Level1] AS [l2.OneToOne_Required_FK_Inverse] ON [l2].[Level1_Required_Id] = [l2.OneToOne_Required_FK_Inverse].[Id] +WHERE [l2.OneToOne_Required_FK_Inverse].[Name] = N'L1 03'", Sql); } @@ -1900,14 +1897,11 @@ public override void Projection_select_correct_table_in_subquery_when_materializ Assert.Equal( @"@__p_0: 3 -SELECT [t].[Name] -FROM ( - SELECT TOP(@__p_0) [l1].* - FROM [Level2] AS [l2] - INNER JOIN [Level1] AS [l1] ON [l2].[Level1_Required_Id] = [l1].[Id] - INNER JOIN [Level3] AS [l3] ON [l1].[Id] = [l3].[Level2_Required_Id] - WHERE ([l1].[Name] = N'L1 03') AND ([l3].[Name] = N'L3 08') -) AS [t]", +SELECT TOP(@__p_0) [l1].[Name] +FROM [Level2] AS [l2] +INNER JOIN [Level1] AS [l1] ON [l2].[Level1_Required_Id] = [l1].[Id] +INNER JOIN [Level3] AS [l3] ON [l1].[Id] = [l3].[Level2_Required_Id] +WHERE ([l1].[Name] = N'L1 03') AND ([l3].[Name] = N'L3 08')", Sql); } @@ -2324,12 +2318,9 @@ public override void Contains_with_subquery_optional_navigation_and_constant_ite FROM [Level1] AS [l1] LEFT JOIN [Level2] AS [l1.OneToOne_Optional_FK] ON [l1].[Id] = [l1.OneToOne_Optional_FK].[Level1_Optional_Id] WHERE 1 IN ( - SELECT [t].[Id] - FROM ( - SELECT DISTINCT [l].* - FROM [Level3] AS [l] - WHERE [l1.OneToOne_Optional_FK].[Id] = [l].[OneToMany_Optional_InverseId] - ) AS [t] + SELECT DISTINCT [l].[Id] + FROM [Level3] AS [l] + WHERE [l1.OneToOne_Optional_FK].[Id] = [l].[OneToMany_Optional_InverseId] )", Sql); } @@ -2350,15 +2341,10 @@ public override void GroupJoin_on_left_side_being_a_subquery() Assert.Equal( @"@__p_0: 2 -SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_InverseId], [t].[OneToMany_Required_Self_InverseId], [t].[OneToOne_Optional_SelfId], [t].[c0], [t].[c1], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[c2], [t].[OneToMany_Optional_InverseId], [t].[c3], [t].[OneToMany_Required_InverseId], [t].[c4], [t].[OneToOne_Optional_PK_InverseId], [t].[c5], [x.OneToOne_Optional_FK].[Id], [x.OneToOne_Optional_FK].[Date], [x.OneToOne_Optional_FK].[Level1_Optional_Id], [x.OneToOne_Optional_FK].[Level1_Required_Id], [x.OneToOne_Optional_FK].[Name], [x.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [x.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [x.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [x.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [x.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [x.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] -FROM ( - SELECT TOP(@__p_0) [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l1.OneToOne_Optional_FK].[Id] AS [c0], [l1.OneToOne_Optional_FK].[Date] AS [c1], [l1.OneToOne_Optional_FK].[Level1_Optional_Id], [l1.OneToOne_Optional_FK].[Level1_Required_Id], [l1.OneToOne_Optional_FK].[Name] AS [c2], [l1.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId] AS [c3], [l1.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId] AS [c4], [l1.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [l1.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] AS [c5] - FROM [Level1] AS [l1] - LEFT JOIN [Level2] AS [l1.OneToOne_Optional_FK] ON [l1].[Id] = [l1.OneToOne_Optional_FK].[Level1_Optional_Id] - ORDER BY [l1.OneToOne_Optional_FK].[Name] -) AS [t] -LEFT JOIN [Level2] AS [x.OneToOne_Optional_FK] ON [t].[Id] = [x.OneToOne_Optional_FK].[Level1_Optional_Id] -ORDER BY [t].[Name]", +SELECT TOP(@__p_0) [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l1.OneToOne_Optional_FK].[Id], [l1.OneToOne_Optional_FK].[Date], [l1.OneToOne_Optional_FK].[Level1_Optional_Id], [l1.OneToOne_Optional_FK].[Level1_Required_Id], [l1.OneToOne_Optional_FK].[Name], [l1.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [l1.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [l1.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] +FROM [Level1] AS [l1] +LEFT JOIN [Level2] AS [l1.OneToOne_Optional_FK] ON [l1].[Id] = [l1.OneToOne_Optional_FK].[Level1_Optional_Id] +ORDER BY [l1.OneToOne_Optional_FK].[Name]", Sql); } @@ -2538,13 +2524,10 @@ public override void Optional_navigation_in_subquery_with_unrelated_projection() Assert.Equal( @"@__p_0: 15 -SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_InverseId], [t].[OneToMany_Required_Self_InverseId], [t].[OneToOne_Optional_SelfId], [t].[c0], [t].[c1], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[c2], [t].[OneToMany_Optional_InverseId], [t].[c3], [t].[OneToMany_Required_InverseId], [t].[c4], [t].[OneToOne_Optional_PK_InverseId], [t].[c5] -FROM ( - SELECT TOP(@__p_0) [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l1.OneToOne_Optional_FK].[Id] AS [c0], [l1.OneToOne_Optional_FK].[Date] AS [c1], [l1.OneToOne_Optional_FK].[Level1_Optional_Id], [l1.OneToOne_Optional_FK].[Level1_Required_Id], [l1.OneToOne_Optional_FK].[Name] AS [c2], [l1.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId] AS [c3], [l1.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId] AS [c4], [l1.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [l1.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] AS [c5] - FROM [Level1] AS [l1] - LEFT JOIN [Level2] AS [l1.OneToOne_Optional_FK] ON [l1].[Id] = [l1.OneToOne_Optional_FK].[Level1_Optional_Id] - WHERE ([l1.OneToOne_Optional_FK].[Name] <> N'Foo') OR [l1.OneToOne_Optional_FK].[Name] IS NULL -) AS [t]", +SELECT TOP(@__p_0) [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l1.OneToOne_Optional_FK].[Id], [l1.OneToOne_Optional_FK].[Date], [l1.OneToOne_Optional_FK].[Level1_Optional_Id], [l1.OneToOne_Optional_FK].[Level1_Required_Id], [l1.OneToOne_Optional_FK].[Name], [l1.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [l1.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [l1.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] +FROM [Level1] AS [l1] +LEFT JOIN [Level2] AS [l1.OneToOne_Optional_FK] ON [l1].[Id] = [l1.OneToOne_Optional_FK].[Level1_Optional_Id] +WHERE ([l1.OneToOne_Optional_FK].[Name] <> N'Foo') OR [l1.OneToOne_Optional_FK].[Name] IS NULL", Sql); } @@ -2555,13 +2538,10 @@ public override void Explicit_GroupJoin_in_subquery_with_unrelated_projection() Assert.Equal( @"@__p_0: 15 -SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_InverseId], [t].[OneToMany_Required_Self_InverseId], [t].[OneToOne_Optional_SelfId], [t].[c0], [t].[c1], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[c2], [t].[OneToMany_Optional_InverseId], [t].[c3], [t].[OneToMany_Required_InverseId], [t].[c4], [t].[OneToOne_Optional_PK_InverseId], [t].[c5] -FROM ( - SELECT TOP(@__p_0) [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l2].[Id] AS [c0], [l2].[Date] AS [c1], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name] AS [c2], [l2].[OneToMany_Optional_InverseId], [l2].[OneToMany_Optional_Self_InverseId] AS [c3], [l2].[OneToMany_Required_InverseId], [l2].[OneToMany_Required_Self_InverseId] AS [c4], [l2].[OneToOne_Optional_PK_InverseId], [l2].[OneToOne_Optional_SelfId] AS [c5] - FROM [Level1] AS [l1] - LEFT JOIN [Level2] AS [l2] ON [l1].[Id] = [l2].[Level1_Optional_Id] - WHERE ([l2].[Name] <> N'Foo') OR [l2].[Name] IS NULL -) AS [t]", +SELECT TOP(@__p_0) [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_InverseId], [l2].[OneToMany_Optional_Self_InverseId], [l2].[OneToMany_Required_InverseId], [l2].[OneToMany_Required_Self_InverseId], [l2].[OneToOne_Optional_PK_InverseId], [l2].[OneToOne_Optional_SelfId] +FROM [Level1] AS [l1] +LEFT JOIN [Level2] AS [l2] ON [l1].[Id] = [l2].[Level1_Optional_Id] +WHERE ([l2].[Name] <> N'Foo') OR [l2].[Name] IS NULL", Sql); } @@ -2570,10 +2550,10 @@ public override void Explicit_GroupJoin_in_subquery_with_unrelated_projection2() base.Explicit_GroupJoin_in_subquery_with_unrelated_projection2(); Assert.Equal( - @"SELECT [l10].[Id], [l10].[Date], [l10].[Name], [l10].[OneToMany_Optional_Self_InverseId], [l10].[OneToMany_Required_Self_InverseId], [l10].[OneToOne_Optional_SelfId], [l20].[Id], [l20].[Date], [l20].[Level1_Optional_Id], [l20].[Level1_Required_Id], [l20].[Name], [l20].[OneToMany_Optional_InverseId], [l20].[OneToMany_Optional_Self_InverseId], [l20].[OneToMany_Required_InverseId], [l20].[OneToMany_Required_Self_InverseId], [l20].[OneToOne_Optional_PK_InverseId], [l20].[OneToOne_Optional_SelfId] -FROM [Level1] AS [l10] -LEFT JOIN [Level2] AS [l20] ON [l10].[Id] = [l20].[Level1_Optional_Id] -WHERE ([l20].[Name] <> N'Foo') OR [l20].[Name] IS NULL", + @"SELECT [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_InverseId], [l2].[OneToMany_Optional_Self_InverseId], [l2].[OneToMany_Required_InverseId], [l2].[OneToMany_Required_Self_InverseId], [l2].[OneToOne_Optional_PK_InverseId], [l2].[OneToOne_Optional_SelfId] +FROM [Level1] AS [l1] +LEFT JOIN [Level2] AS [l2] ON [l1].[Id] = [l2].[Level1_Optional_Id] +WHERE ([l2].[Name] <> N'Foo') OR [l2].[Name] IS NULL", Sql); } @@ -2645,17 +2625,13 @@ public override void Where_on_multilevel_reference_in_subquery_with_outer_projec @"@__p_0: 0 @__p_1: 10 -SELECT [t].[Name] -FROM ( - SELECT [l3].* - FROM [Level3] AS [l3] - INNER JOIN [Level2] AS [l3.OneToMany_Required_Inverse] ON [l3].[OneToMany_Required_InverseId] = [l3.OneToMany_Required_Inverse].[Id] - INNER JOIN [Level1] AS [l3.OneToMany_Required_Inverse.OneToOne_Required_FK_Inverse] ON [l3.OneToMany_Required_Inverse].[Level1_Required_Id] = [l3.OneToMany_Required_Inverse.OneToOne_Required_FK_Inverse].[Id] - WHERE [l3.OneToMany_Required_Inverse.OneToOne_Required_FK_Inverse].[Name] = N'L1 03' - ORDER BY [l3].[Level2_Required_Id] - OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY -) AS [t] -ORDER BY [t].[Level2_Required_Id]", +SELECT [l3].[Name] +FROM [Level3] AS [l3] +INNER JOIN [Level2] AS [l3.OneToMany_Required_Inverse] ON [l3].[OneToMany_Required_InverseId] = [l3.OneToMany_Required_Inverse].[Id] +INNER JOIN [Level1] AS [l3.OneToMany_Required_Inverse.OneToOne_Required_FK_Inverse] ON [l3.OneToMany_Required_Inverse].[Level1_Required_Id] = [l3.OneToMany_Required_Inverse.OneToOne_Required_FK_Inverse].[Id] +WHERE [l3.OneToMany_Required_Inverse.OneToOne_Required_FK_Inverse].[Name] = N'L1 03' +ORDER BY [l3].[Level2_Required_Id] +OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY", Sql); } diff --git a/test/EFCore.SqlServer.FunctionalTests/IncludeSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/IncludeSqlServerTest.cs index 0bfa190e4ac..41cfc00f5d2 100644 --- a/test/EFCore.SqlServer.FunctionalTests/IncludeSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/IncludeSqlServerTest.cs @@ -937,15 +937,11 @@ public override void Include_where_skip_take_projection(bool useString) @__p_1: 2 SELECT [od.Order].[CustomerID] -FROM ( - SELECT [od].* - FROM [Order Details] AS [od] - WHERE [od].[Quantity] = 10 - ORDER BY [od].[OrderID], [od].[ProductID] - OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY -) AS [t] -INNER JOIN [Orders] AS [od.Order] ON [t].[OrderID] = [od.Order].[OrderID] -ORDER BY [t].[OrderID], [t].[ProductID]", +FROM [Order Details] AS [od] +INNER JOIN [Orders] AS [od.Order] ON [od].[OrderID] = [od.Order].[OrderID] +WHERE [od].[Quantity] = 10 +ORDER BY [od].[OrderID], [od].[ProductID] +OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY", Sql); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs index 1bdbde4a390..e6e7a13eaab 100644 --- a/test/EFCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs @@ -198,13 +198,9 @@ public override void Take_Select_Navigation() Assert.Equal( @"@__p_0: 2 -SELECT [t].[CustomerID] -FROM ( - SELECT TOP(@__p_0) [c].* - FROM [Customers] AS [c] - ORDER BY [c].[CustomerID] -) AS [t] -ORDER BY [t].[CustomerID] +SELECT TOP(@__p_0) [c].[CustomerID] +FROM [Customers] AS [c] +ORDER BY [c].[CustomerID] @_outer_CustomerID: ALFKI (Size = 450) @@ -227,17 +223,13 @@ public override void Select_collection_FirstOrDefault_project_single_column1() Assert.Equal( @"@__p_0: 2 -SELECT ( +SELECT TOP(@__p_0) ( SELECT TOP(1) [o].[CustomerID] FROM [Orders] AS [o] - WHERE [t].[CustomerID] = [o].[CustomerID] + WHERE [c].[CustomerID] = [o].[CustomerID] ) -FROM ( - SELECT TOP(@__p_0) [c].* - FROM [Customers] AS [c] - ORDER BY [c].[CustomerID] -) AS [t] -ORDER BY [t].[CustomerID]", +FROM [Customers] AS [c] +ORDER BY [c].[CustomerID]", Sql); } @@ -248,17 +240,13 @@ public override void Select_collection_FirstOrDefault_project_single_column2() Assert.Equal( @"@__p_0: 2 -SELECT ( +SELECT TOP(@__p_0) ( SELECT TOP(1) [o].[CustomerID] FROM [Orders] AS [o] - WHERE [t].[CustomerID] = [o].[CustomerID] + WHERE [c].[CustomerID] = [o].[CustomerID] ) -FROM ( - SELECT TOP(@__p_0) [c].* - FROM [Customers] AS [c] - ORDER BY [c].[CustomerID] -) AS [t] -ORDER BY [t].[CustomerID]", +FROM [Customers] AS [c] +ORDER BY [c].[CustomerID]", Sql); } @@ -269,13 +257,9 @@ public override void Select_collection_FirstOrDefault_project_anonymous_type() Assert.Equal( @"@__p_0: 2 -SELECT [t].[CustomerID] -FROM ( - SELECT TOP(@__p_0) [c].* - FROM [Customers] AS [c] - ORDER BY [c].[CustomerID] -) AS [t] -ORDER BY [t].[CustomerID] +SELECT TOP(@__p_0) [c].[CustomerID] +FROM [Customers] AS [c] +ORDER BY [c].[CustomerID] @_outer_CustomerID: ALFKI (Size = 450) @@ -298,13 +282,9 @@ public override void Select_collection_FirstOrDefault_project_entity() Assert.Equal( @"@__p_0: 2 -SELECT [t].[CustomerID] -FROM ( - SELECT TOP(@__p_0) [c].* - FROM [Customers] AS [c] - ORDER BY [c].[CustomerID] -) AS [t] -ORDER BY [t].[CustomerID] +SELECT TOP(@__p_0) [c].[CustomerID] +FROM [Customers] AS [c] +ORDER BY [c].[CustomerID] @_outer_CustomerID: ALFKI (Size = 450) @@ -329,14 +309,10 @@ public override void Skip_Select_Navigation() Assert.StartsWith( @"@__p_0: 20 -SELECT [t].[CustomerID] -FROM ( - SELECT [c].* - FROM [Customers] AS [c] - ORDER BY [c].[CustomerID] - OFFSET @__p_0 ROWS -) AS [t] -ORDER BY [t].[CustomerID] +SELECT [c].[CustomerID] +FROM [Customers] AS [c] +ORDER BY [c].[CustomerID] +OFFSET @__p_0 ROWS @_outer_CustomerID: FAMIA (Size = 450) diff --git a/test/EFCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs index 56a1083f2d4..8f35e66f914 100644 --- a/test/EFCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs @@ -1745,11 +1745,8 @@ public override void Queryable_simple_anonymous_projection_subquery() Assert.Equal( @"@__p_0: 91 -SELECT [t].[City] -FROM ( - SELECT TOP(@__p_0) [c].* - FROM [Customers] AS [c] -) AS [t]", +SELECT TOP(@__p_0) [c].[City] +FROM [Customers] AS [c]", Sql); } @@ -1811,13 +1808,9 @@ public override void Take_subquery_projection() Assert.Equal( @"@__p_0: 2 -SELECT [t].[City] -FROM ( - SELECT TOP(@__p_0) [c].* - FROM [Customers] AS [c] - ORDER BY [c].[CustomerID] -) AS [t] -ORDER BY [t].[CustomerID]", +SELECT TOP(@__p_0) [c].[City] +FROM [Customers] AS [c] +ORDER BY [c].[CustomerID]", Sql); } @@ -2141,11 +2134,8 @@ public override void Select_scalar_primitive_after_take() Assert.Equal( @"@__p_0: 9 -SELECT [t].[EmployeeID] -FROM ( - SELECT TOP(@__p_0) [e].* - FROM [Employees] AS [e] -) AS [t]", +SELECT TOP(@__p_0) [e].[EmployeeID] +FROM [Employees] AS [e]", Sql); } @@ -5590,12 +5580,9 @@ FROM [Customers] AS [c] @_outer_CustomerID: ALFKI (Size = 450) -SELECT [t].[OrderDate] -FROM ( - SELECT TOP(3) [o].* - FROM [Orders] AS [o] - WHERE ([o].[OrderID] < 10500) AND (@_outer_CustomerID = [o].[CustomerID]) -) AS [t]", +SELECT TOP(3) [o].[OrderDate] +FROM [Orders] AS [o] +WHERE ([o].[OrderID] < 10500) AND (@_outer_CustomerID = [o].[CustomerID])", Sql); } @@ -5611,15 +5598,12 @@ FROM [Customers] AS [c] @_outer_City: Berlin (Size = 6) @_outer_CustomerID: ALFKI (Size = 450) -SELECT [t].[OrderDate] -FROM ( - SELECT TOP(3) [o].* - FROM [Orders] AS [o] - WHERE EXISTS ( - SELECT 1 - FROM [Order Details] AS [d] - WHERE ([d].[Discount] > LEN(@_outer_City)) AND ([o].[OrderID] = [d].[OrderID])) AND (@_outer_CustomerID = [o].[CustomerID]) -) AS [t]", +SELECT TOP(3) [o].[OrderDate] +FROM [Orders] AS [o] +WHERE EXISTS ( + SELECT 1 + FROM [Order Details] AS [d] + WHERE ([d].[Discount] > LEN(@_outer_City)) AND ([o].[OrderID] = [d].[OrderID])) AND (@_outer_CustomerID = [o].[CustomerID])", Sql); } @@ -5647,12 +5631,9 @@ THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) @_outer_CustomerID: ALFKI (Size = 450) -SELECT [t].[OrderDate] -FROM ( - SELECT TOP(3) [o0].* - FROM [Orders] AS [o0] - WHERE @_outer_CustomerID = [o0].[CustomerID] -) AS [t]", +SELECT TOP(3) [o0].[OrderDate] +FROM [Orders] AS [o0] +WHERE @_outer_CustomerID = [o0].[CustomerID]", Sql); }