From 447f48e81aac80bde0d19edfa27e7b942db371df Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Wed, 18 Dec 2019 13:55:42 -0800 Subject: [PATCH] Query: Improvements to Navigation Expansion - Skip/Take does not force applying pending selector and changing shape. - Throw translation failure message for Querayble methods which we don't translate (hence we don't process in navigation expansion). Earlier we threw query failed message. Now Navigation Expansion does not throw QueryFailed error message from any place. - Unwrap type conversion for validating member access during include expansion so that we don't generate include when derived type's member is accessed. Resolves #18140 Resolves #18374 Resolves #18672 Resolves #18734 Resolves #19138 Resolves #19207 --- .../Query/Internal/QuerySqlGenerator.cs | 3 +- ...ingExpressionVisitor.ExpressionVisitors.cs | 25 +- ...nExpandingExpressionVisitor.Expressions.cs | 5 - .../NavigationExpandingExpressionVisitor.cs | 283 +++---- ...yableMethodTranslatingExpressionVisitor.cs | 8 +- src/Shared/EnumerableMethods.cs | 3 + src/Shared/ExpressionExtensions.cs | 4 +- .../NorthwindMiscellaneousQueryCosmosTest.cs | 51 ++ .../Query/OwnedQueryCosmosTest.cs | 12 + .../Query/QueryNoClientEvalTestBase.cs | 45 +- .../Query/GearsOfWarQueryTestBase.cs | 20 +- .../Query/NorthwindIncludeQueryTestBase.cs | 74 ++ .../NorthwindMiscellaneousQueryTestBase.cs | 240 +++++- .../Query/NorthwindWhereQueryTestBase.cs | 93 +++ .../Query/OwnedQueryTestBase.cs | 62 ++ .../ComplexNavigationsQuerySqlServerTest.cs | 64 +- ...omplexNavigationsWeakQuerySqlServerTest.cs | 18 +- .../Query/GearsOfWarQuerySqlServerTest.cs | 11 +- .../NorthwindIncludeQuerySqlServerTest.cs | 76 ++ ...orthwindMiscellaneousQuerySqlServerTest.cs | 179 ++++- .../NorthwindNavigationsQuerySqlServerTest.cs | 14 +- .../Query/NorthwindWhereQuerySqlServerTest.cs | 2 +- .../Query/OwnedQuerySqlServerTest.cs | 727 +++++++++++++++++- .../Query/QueryBugsTest.cs | 89 ++- 24 files changed, 1767 insertions(+), 341 deletions(-) diff --git a/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs b/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs index 3d34617ad04..11656852406 100644 --- a/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs +++ b/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs @@ -247,7 +247,8 @@ protected override Expression VisitSelect(SelectExpression selectExpression) } else { - throw new InvalidOperationException(CoreStrings.QueryFailed(selectExpression.Print(), GetType().Name)); + // TODO: See Issue#18923 + throw new InvalidOperationException("Cosmos Sql API does not support Offset without Limit."); } } diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index 970dc1d1e68..f5dc4a95ad8 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -246,11 +246,8 @@ protected Expression ExpandNavigation( var outerKeySelector = _navigationExpandingExpressionVisitor.GenerateLambda( outerKey, _source.CurrentParameter); - var innerKeySelector = _navigationExpandingExpressionVisitor.GenerateLambda( - _navigationExpandingExpressionVisitor.ExpandNavigationsInLambdaExpression( - innerSource, - Expression.Lambda(innerKey, innerParameter)), - innerSource.CurrentParameter); + var innerKeySelector = _navigationExpandingExpressionVisitor.ProcessLambdaExpression( + innerSource, Expression.Lambda(innerKey, innerParameter)); var resultSelectorOuterParameter = Expression.Parameter(_source.SourceElementType, "o"); var resultSelectorInnerParameter = Expression.Parameter(innerSource.SourceElementType, "i"); @@ -349,10 +346,22 @@ protected override Expression VisitMember(MemberExpression memberExpression) { Check.NotNull(memberExpression, nameof(memberExpression)); - if (UnwrapEntityReference(memberExpression.Expression) is EntityReference entityReferece) + var innerExpression = memberExpression.Expression.UnwrapTypeConversion(out var convertedType); + if (UnwrapEntityReference(innerExpression) is EntityReference entityReference) { // If it is mapped property then, it would get converted to a column so we don't need to expand includes. - var property = entityReferece.EntityType.FindProperty(memberExpression.Member); + var entityType = entityReference.EntityType; + if (convertedType != null) + { + entityType = entityType.GetTypesInHierarchy() + .FirstOrDefault(et => et.ClrType == convertedType); + if (entityType == null) + { + return base.VisitMember(memberExpression); + } + } + + var property = entityType.FindProperty(memberExpression.Member); if (property != null) { return memberExpression; @@ -366,7 +375,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp { Check.NotNull(methodCallExpression, nameof(methodCallExpression)); - if (methodCallExpression.TryGetEFPropertyArguments(out var _, out var __)) + if (methodCallExpression.TryGetEFPropertyArguments(out _, out _)) { // If it is EF.Property then, it would get converted to a column or throw // so we don't need to expand includes. diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs index a4ab8d7ce48..582f66d43c0 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs @@ -6,8 +6,6 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Utilities; @@ -316,9 +314,6 @@ private set public virtual NavigationTreeNode Right { get; } public virtual ParameterExpression CurrentParameter { get; private set; } - protected override Expression VisitChildren(ExpressionVisitor visitor) - => throw new InvalidOperationException(CoreStrings.QueryFailed(this.Print(), GetType().Name)); - public virtual void SetParameter(string parameterName) => CurrentParameter = Parameter(Type, parameterName); public override ExpressionType NodeType => ExpressionType.Extension; diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 74d63468aeb..7c3cd0c7e44 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -240,16 +240,16 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp case nameof(Queryable.Average) when QueryableMethods.IsAverageWithoutSelector(method): - case nameof(Queryable.Sum) - when QueryableMethods.IsSumWithoutSelector(method): case nameof(Queryable.Max) when genericMethod == QueryableMethods.MaxWithoutSelector: case nameof(Queryable.Min) when genericMethod == QueryableMethods.MinWithoutSelector: + case nameof(Queryable.Sum) + when QueryableMethods.IsSumWithoutSelector(method): return ProcessAverageMaxMinSum( source, genericMethod ?? method, - null); + selector: null); case nameof(Queryable.Average) when QueryableMethods.IsAverageWithSelector(method): @@ -266,13 +266,13 @@ when QueryableMethods.IsSumWithSelector(method): case nameof(Queryable.Distinct) when genericMethod == QueryableMethods.Distinct: - return ProcessDistinctSkipTake(source, genericMethod, null); + return ProcessDistinct(source, genericMethod); case nameof(Queryable.Skip) when genericMethod == QueryableMethods.Skip: case nameof(Queryable.Take) when genericMethod == QueryableMethods.Take: - return ProcessDistinctSkipTake( + return ProcessSkipTake( source, genericMethod, methodCallExpression.Arguments[1]); @@ -298,7 +298,7 @@ when QueryableMethods.IsSumWithSelector(method): return ProcessFirstSingleLastOrDefault( source, genericMethod, - null, + predicate: null, methodCallExpression.Type); case nameof(Queryable.First) @@ -333,7 +333,7 @@ when QueryableMethods.IsSumWithSelector(method): methodCallExpression.Arguments[4].UnwrapLambdaFromQuote()); } - break; + goto default; } case nameof(QueryableExtensions.LeftJoin) @@ -350,7 +350,7 @@ when QueryableMethods.IsSumWithSelector(method): methodCallExpression.Arguments[4].UnwrapLambdaFromQuote()); } - break; + goto default; } case nameof(Queryable.SelectMany) @@ -379,13 +379,10 @@ when QueryableMethods.IsSumWithSelector(method): var secondArgument = Visit(methodCallExpression.Arguments[1]); if (secondArgument is NavigationExpansionExpression innerSource) { - return ProcessSetOperation( - source, - genericMethod, - innerSource); + return ProcessSetOperation(source, genericMethod, innerSource); } - break; + goto default; } case nameof(Queryable.Cast) @@ -479,7 +476,18 @@ when QueryableMethods.IsSumWithSelector(method): return ProcessDefaultIfEmpty(source); default: - throw new InvalidOperationException(CoreStrings.QueryFailed(methodCallExpression.Print(), GetType().Name)); + // Aggregate overloads + // GroupJoin overloads + // Zip + // SequenceEqual overloads + // ElementAt + // ElementAtOrDefault + // SkipWhile + // TakeWhile + // DefaultIfEmpty with argument + // Index based lambda overloads of Where, SkipWhile, TakeWhile, Select, SelectMany + // IEqualityComparer overloads of Distinct, Contains, Join, Except, Intersect, Union, OrderBy, ThenBy, OrderByDescending, ThenByDescending, GroupBy + throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCallExpression.Print())); } } @@ -515,13 +523,12 @@ when QueryableMethods.IsSumWithSelector(method): { // firstArgument was not an queryable var visitedArguments = new[] { firstArgument } - .Concat(methodCallExpression.Arguments.Skip(1).Select(Visit)) - .ToList(); + .Concat(methodCallExpression.Arguments.Skip(1).Select(Visit)); return ConvertToEnumerable(method, visitedArguments); } - throw new InvalidOperationException(CoreStrings.QueryFailed(methodCallExpression.Print(), GetType().Name)); + throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCallExpression.Print())); } if (method.IsGenericMethod @@ -562,27 +569,20 @@ private Expression ProcessAllAnyCountLongCount( { if (predicate != null) { - var predicateBody = ExpandNavigationsInLambdaExpression(source, predicate); + predicate = ProcessLambdaExpression(source, predicate); return Expression.Call( - genericMethod.GetGenericMethodDefinition().MakeGenericMethod(source.SourceElementType), - source.Source, - Expression.Quote(GenerateLambda(predicateBody, source.CurrentParameter))); + genericMethod.MakeGenericMethod(source.SourceElementType), source.Source, Expression.Quote(predicate)); } - return Expression.Call( - genericMethod.MakeGenericMethod(source.SourceElementType), - source.Source); + return Expression.Call(genericMethod.MakeGenericMethod(source.SourceElementType), source.Source); } - private Expression ProcessAverageMaxMinSum( - NavigationExpansionExpression source, - MethodInfo method, - LambdaExpression selector) + private Expression ProcessAverageMaxMinSum(NavigationExpansionExpression source, MethodInfo method, LambdaExpression selector) { if (selector != null) { - source = (NavigationExpansionExpression)ProcessSelect(source, selector); + source = ProcessSelect(source, selector); source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); var selectorLambda = GenerateLambda(source.PendingSelector, source.CurrentParameter); @@ -611,10 +611,10 @@ private Expression ProcessAverageMaxMinSum( return Expression.Call(method, queryable); } - private Expression ProcessCastOfType(NavigationExpansionExpression source, MethodInfo genericMethod, Type castType) + private NavigationExpansionExpression ProcessCastOfType( + NavigationExpansionExpression source, MethodInfo genericMethod, Type castType) { - if ((castType.IsInterface - && castType.IsAssignableFrom(source.PendingSelector.Type)) + if (castType.IsAssignableFrom(source.PendingSelector.Type) || castType == typeof(object)) { // Casting to base/implementing interface is redundant @@ -629,7 +629,7 @@ private Expression ProcessCastOfType(NavigationExpansionExpression source, Metho if (newStructure is EntityReference entityReference && entityReference.EntityType.GetTypesInHierarchy() - .FirstOrDefault(et => et.ClrType == castType) is EntityType castEntityType) + .FirstOrDefault(et => et.ClrType == castType) is IEntityType castEntityType) { var newEntityReference = new EntityReference(castEntityType); if (entityReference.IsOptional) @@ -668,13 +668,10 @@ private Expression ProcessContains(NavigationExpansionExpression source, Express source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); var queryable = Reduce(source); - return Expression.Call( - QueryableMethods.Contains.MakeGenericMethod(queryable.Type.TryGetSequenceType()), - queryable, - item); + return Expression.Call(QueryableMethods.Contains.MakeGenericMethod(queryable.Type.TryGetSequenceType()), queryable, item); } - private Expression ProcessDefaultIfEmpty(NavigationExpansionExpression source) + private NavigationExpansionExpression ProcessDefaultIfEmpty(NavigationExpansionExpression source) { source.UpdateSource( Expression.Call( @@ -686,20 +683,13 @@ private Expression ProcessDefaultIfEmpty(NavigationExpansionExpression source) return source; } - private Expression ProcessDistinctSkipTake(NavigationExpansionExpression source, MethodInfo genericMethod, Expression count) + private NavigationExpansionExpression ProcessDistinct(NavigationExpansionExpression source, MethodInfo genericMethod) { source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); var newStructure = SnapshotExpression(source.PendingSelector); var queryable = Reduce(source); - var result = count == null - ? Expression.Call( - genericMethod.MakeGenericMethod(queryable.Type.TryGetSequenceType()), - queryable) - : Expression.Call( - genericMethod.MakeGenericMethod(queryable.Type.TryGetSequenceType()), - queryable, - count); + var result = Expression.Call(genericMethod.MakeGenericMethod(queryable.Type.TryGetSequenceType()), queryable); var navigationTree = new NavigationTreeExpression(newStructure); var parameterName = GetParameterName("e"); @@ -707,19 +697,20 @@ private Expression ProcessDistinctSkipTake(NavigationExpansionExpression source, return new NavigationExpansionExpression(result, navigationTree, navigationTree, parameterName); } - private Expression ProcessFirstSingleLastOrDefault( + private NavigationExpansionExpression ProcessSkipTake( + NavigationExpansionExpression source, MethodInfo genericMethod, Expression count) + { + source.UpdateSource(Expression.Call(genericMethod.MakeGenericMethod(source.SourceElementType), source.Source, count)); + + return source; + } + + private NavigationExpansionExpression ProcessFirstSingleLastOrDefault( NavigationExpansionExpression source, MethodInfo genericMethod, LambdaExpression predicate, Type returnType) { if (predicate != null) { - var predicateBody = ExpandNavigationsInLambdaExpression(source, predicate); - - source.UpdateSource( - Expression.Call( - QueryableMethods.Where.MakeGenericMethod(source.SourceElementType), - source.Source, - Expression.Quote(GenerateLambda(predicateBody, source.CurrentParameter)))); - + source = ProcessWhere(source, predicate); genericMethod = _predicateLessMethodInfo[genericMethod]; } @@ -733,75 +724,56 @@ private Expression ProcessFirstSingleLastOrDefault( return source; } - private Expression ProcessGroupBy( + private NavigationExpansionExpression ProcessGroupBy( NavigationExpansionExpression source, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector) { - var keySelectorBody = ExpandNavigationsInLambdaExpression(source, keySelector); + var keySelectorBody = ExpandNavigationsForSource(source, RemapLambdaExpression(source, keySelector)); + // Need to generate lambda after processing element/result selector Expression result; - if (elementSelector == null) + if (elementSelector != null) { - source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); - // TODO: Flow include in future - //source = (NavigationExpansionExpression)new IncludeApplyingExpressionVisitor( - // this, _queryCompilationContext.IsTracking).Visit(source); - keySelector = GenerateLambda(keySelectorBody, source.CurrentParameter); - elementSelector = GenerateLambda(source.PendingSelector, source.CurrentParameter); - result = resultSelector == null - ? Expression.Call( - QueryableMethods.GroupByWithKeyElementSelector.MakeGenericMethod( - source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType), - source.Source, - Expression.Quote(keySelector), - Expression.Quote(elementSelector)) - : Expression.Call( - QueryableMethods.GroupByWithKeyElementResultSelector.MakeGenericMethod( - source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType, resultSelector.ReturnType), - source.Source, - Expression.Quote(keySelector), - Expression.Quote(elementSelector), - Expression.Quote(resultSelector)); - } - else - { - source = (NavigationExpansionExpression)ProcessSelect(source, elementSelector); - source = (NavigationExpansionExpression)new PendingSelectorExpandingExpressionVisitor(this, applyIncludes: true) - .Visit(source); - keySelector = GenerateLambda(keySelectorBody, source.CurrentParameter); - elementSelector = GenerateLambda(source.PendingSelector, source.CurrentParameter); - result = resultSelector == null - ? Expression.Call( - QueryableMethods.GroupByWithKeyElementSelector.MakeGenericMethod( - source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType), - source.Source, - Expression.Quote(keySelector), - Expression.Quote(elementSelector)) - : Expression.Call( - QueryableMethods.GroupByWithKeyElementResultSelector.MakeGenericMethod( - source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType, resultSelector.ReturnType), - source.Source, - Expression.Quote(keySelector), - Expression.Quote(elementSelector), - Expression.Quote(resultSelector)); + source = ProcessSelect(source, elementSelector); } + source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); + // TODO: Flow include in future + //source = (NavigationExpansionExpression)new IncludeApplyingExpressionVisitor( + // this, _queryCompilationContext.IsTracking).Visit(source); + keySelector = GenerateLambda(keySelectorBody, source.CurrentParameter); + elementSelector = GenerateLambda(source.PendingSelector, source.CurrentParameter); + result = resultSelector == null + ? Expression.Call( + QueryableMethods.GroupByWithKeyElementSelector.MakeGenericMethod( + source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType), + source.Source, + Expression.Quote(keySelector), + Expression.Quote(elementSelector)) + : Expression.Call( + QueryableMethods.GroupByWithKeyElementResultSelector.MakeGenericMethod( + source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType, resultSelector.ReturnType), + source.Source, + Expression.Quote(keySelector), + Expression.Quote(elementSelector), + Expression.Quote(resultSelector)); + var navigationTree = new NavigationTreeExpression(Expression.Default(result.Type.TryGetSequenceType())); var parameterName = GetParameterName("e"); return new NavigationExpansionExpression(result, navigationTree, navigationTree, parameterName); } - private Expression ProcessInclude(NavigationExpansionExpression source, Expression expression, bool thenInclude) + private NavigationExpansionExpression ProcessInclude(NavigationExpansionExpression source, Expression expression, bool thenInclude) { if (source.PendingSelector is NavigationTreeExpression navigationTree - && navigationTree.Value is EntityReference entityReferece) + && navigationTree.Value is EntityReference entityReference) { - if (entityReferece.EntityType.GetDefiningQuery() != null) + if (entityReference.EntityType.GetDefiningQuery() != null) { throw new InvalidOperationException( - CoreStrings.IncludeOnEntityWithDefiningQueryNotSupported(entityReferece.EntityType.DisplayName())); + CoreStrings.IncludeOnEntityWithDefiningQueryNotSupported(entityReference.EntityType.DisplayName())); } if (expression is ConstantExpression includeConstant @@ -809,7 +781,7 @@ private Expression ProcessInclude(NavigationExpansionExpression source, Expressi { var navigationPaths = navigationChain.Split(new[] { "." }, StringSplitOptions.None); var includeTreeNodes = new Queue(); - includeTreeNodes.Enqueue(entityReferece.IncludePaths); + includeTreeNodes.Enqueue(entityReference.IncludePaths); foreach (var navigationName in navigationPaths) { var nodesToProcess = includeTreeNodes.Count; @@ -835,8 +807,8 @@ private Expression ProcessInclude(NavigationExpansionExpression source, Expressi else { var currentIncludeTreeNode = thenInclude - ? entityReferece.LastIncludeTreeNode - : entityReferece.IncludePaths; + ? entityReference.LastIncludeTreeNode + : entityReference.IncludePaths; var includeLambda = expression.UnwrapLambdaFromQuote(); var lastIncludeTree = PopulateIncludeTree(currentIncludeTreeNode, includeLambda.Body); if (lastIncludeTree == null) @@ -844,7 +816,7 @@ private Expression ProcessInclude(NavigationExpansionExpression source, Expressi throw new InvalidOperationException("Lambda expression used inside Include is not valid."); } - entityReferece.SetLastInclude(lastIncludeTree); + entityReference.SetLastInclude(lastIncludeTree); } return source; @@ -853,7 +825,7 @@ private Expression ProcessInclude(NavigationExpansionExpression source, Expressi throw new InvalidOperationException("Include has been used on non entity queryable."); } - private Expression ProcessJoin( + private NavigationExpansionExpression ProcessJoin( NavigationExpansionExpression outerSource, NavigationExpansionExpression innerSource, LambdaExpression outerKeySelector, @@ -865,11 +837,8 @@ private Expression ProcessJoin( ApplyPendingOrderings(innerSource); } - var outerKey = ExpandNavigationsInLambdaExpression(outerSource, outerKeySelector); - var innerKey = ExpandNavigationsInLambdaExpression(innerSource, innerKeySelector); - - outerKeySelector = GenerateLambda(outerKey, outerSource.CurrentParameter); - innerKeySelector = GenerateLambda(innerKey, innerSource.CurrentParameter); + outerKeySelector = ProcessLambdaExpression(outerSource, outerKeySelector); + innerKeySelector = ProcessLambdaExpression(innerSource, innerKeySelector); var transparentIdentifierType = TransparentIdentifierFactory.Create( outerSource.SourceElementType, innerSource.SourceElementType); @@ -880,15 +849,15 @@ private Expression ProcessJoin( var newResultSelector = Expression.Lambda( Expression.New( transparentIdentifierType.GetConstructors().Single(), - new[] { outerSource.CurrentParameter, innerSource.CurrentParameter }, transparentIdentifierOuterMemberInfo, + new[] { outerSource.CurrentParameter, innerSource.CurrentParameter }, + transparentIdentifierOuterMemberInfo, transparentIdentifierInnerMemberInfo), outerSource.CurrentParameter, innerSource.CurrentParameter); var source = Expression.Call( QueryableMethods.Join.MakeGenericMethod( - outerSource.SourceElementType, innerSource.SourceElementType, outerKeySelector.ReturnType, - newResultSelector.ReturnType), + outerSource.SourceElementType, innerSource.SourceElementType, outerKeySelector.ReturnType, newResultSelector.ReturnType), outerSource.Source, innerSource.Source, Expression.Quote(outerKeySelector), @@ -907,7 +876,7 @@ private Expression ProcessJoin( return new NavigationExpansionExpression(source, currentTree, pendingSelector, parameterName); } - private Expression ProcessLeftJoin( + private NavigationExpansionExpression ProcessLeftJoin( NavigationExpansionExpression outerSource, NavigationExpansionExpression innerSource, LambdaExpression outerKeySelector, @@ -919,11 +888,8 @@ private Expression ProcessLeftJoin( ApplyPendingOrderings(innerSource); } - var outerKey = ExpandNavigationsInLambdaExpression(outerSource, outerKeySelector); - var innerKey = ExpandNavigationsInLambdaExpression(innerSource, innerKeySelector); - - outerKeySelector = GenerateLambda(outerKey, outerSource.CurrentParameter); - innerKeySelector = GenerateLambda(innerKey, innerSource.CurrentParameter); + outerKeySelector = ProcessLambdaExpression(outerSource, outerKeySelector); + innerKeySelector = ProcessLambdaExpression(innerSource, innerKeySelector); var transparentIdentifierType = TransparentIdentifierFactory.Create( outerSource.SourceElementType, innerSource.SourceElementType); @@ -934,15 +900,15 @@ private Expression ProcessLeftJoin( var newResultSelector = Expression.Lambda( Expression.New( transparentIdentifierType.GetConstructors().Single(), - new[] { outerSource.CurrentParameter, innerSource.CurrentParameter }, transparentIdentifierOuterMemberInfo, + new[] { outerSource.CurrentParameter, innerSource.CurrentParameter }, + transparentIdentifierOuterMemberInfo, transparentIdentifierInnerMemberInfo), outerSource.CurrentParameter, innerSource.CurrentParameter); var source = Expression.Call( QueryableExtensions.LeftJoinMethodInfo.MakeGenericMethod( - outerSource.SourceElementType, innerSource.SourceElementType, outerKeySelector.ReturnType, - newResultSelector.ReturnType), + outerSource.SourceElementType, innerSource.SourceElementType, outerKeySelector.ReturnType, newResultSelector.ReturnType), outerSource.Source, innerSource.Source, Expression.Quote(outerKeySelector), @@ -964,7 +930,7 @@ private Expression ProcessLeftJoin( return new NavigationExpansionExpression(source, currentTree, pendingSelector, parameterName); } - private Expression ProcessOrderByThenBy( + private NavigationExpansionExpression ProcessOrderByThenBy( NavigationExpansionExpression source, MethodInfo genericMethod, LambdaExpression keySelector, bool thenBy) { var lambdaBody = ReplacingExpressionVisitor.Replace( @@ -997,7 +963,7 @@ private Expression ProcessReverse(NavigationExpansionExpression source) return source; } - private Expression ProcessSelect(NavigationExpansionExpression source, LambdaExpression selector) + private NavigationExpansionExpression ProcessSelect(NavigationExpansionExpression source, LambdaExpression selector) { // This is to apply aggregate operator on GroupBy right away rather than deferring if (source.SourceElementType.IsGenericType @@ -1005,7 +971,7 @@ private Expression ProcessSelect(NavigationExpansionExpression source, LambdaExp && !(selector.ReturnType.IsGenericType && selector.ReturnType.GetGenericTypeDefinition() == typeof(IGrouping<,>))) { - var selectorLambda = GenerateLambda(ExpandNavigationsInLambdaExpression(source, selector), source.CurrentParameter); + var selectorLambda = ProcessLambdaExpression(source, selector); var newSource = Expression.Call( QueryableMethods.Select.MakeGenericMethod(source.SourceElementType, selectorLambda.ReturnType), source.Source, @@ -1027,10 +993,10 @@ private Expression ProcessSelect(NavigationExpansionExpression source, LambdaExp return source; } - private Expression ProcessSelectMany( + private NavigationExpansionExpression ProcessSelectMany( NavigationExpansionExpression source, LambdaExpression collectionSelector, LambdaExpression resultSelector) { - var collectionSelectorBody = ExpandNavigationsInLambdaExpression(source, collectionSelector); + var collectionSelectorBody = ExpandNavigationsForSource(source, RemapLambdaExpression(source, collectionSelector)); if (collectionSelectorBody is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression) { collectionSelectorBody = materializeCollectionNavigationExpression.Subquery; @@ -1088,10 +1054,11 @@ private Expression ProcessSelectMany( return new NavigationExpansionExpression(newSource, currentTree, pendingSelector, parameterName); } - throw new InvalidOperationException(CoreStrings.QueryFailed(collectionSelector.Print(), GetType().Name)); + // TODO: Improve this exception message + throw new InvalidOperationException(CoreStrings.TranslationFailed(collectionSelector.Print())); } - private Expression ProcessSetOperation( + private NavigationExpansionExpression ProcessSetOperation( NavigationExpansionExpression outerSource, MethodInfo genericMethod, NavigationExpansionExpression innerSource) { outerSource = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(outerSource); @@ -1156,15 +1123,15 @@ private Expression ProcessUnknownMethod(MethodCallExpression methodCallExpressio return base.VisitMethodCall(methodCallExpression); } - private Expression ProcessWhere(NavigationExpansionExpression source, LambdaExpression predicate) + private NavigationExpansionExpression ProcessWhere(NavigationExpansionExpression source, LambdaExpression predicate) { - var predicateBody = ExpandNavigationsInLambdaExpression(source, predicate); + predicate = ProcessLambdaExpression(source, predicate); source.UpdateSource( Expression.Call( QueryableMethods.Where.MakeGenericMethod(source.SourceElementType), source.Source, - Expression.Quote(GenerateLambda(predicateBody, source.CurrentParameter)))); + Expression.Quote(predicate))); return source; } @@ -1277,7 +1244,7 @@ private bool CompareIncludes(Expression outer, Expression inner) && outerDefaultExpression.Type == innerDefaultExpression.Type; } - private MethodCallExpression ConvertToEnumerable(MethodInfo queryableMethod, List arguments) + private MethodCallExpression ConvertToEnumerable(MethodInfo queryableMethod, IEnumerable arguments) { var genericTypeArguments = queryableMethod.IsGenericMethod ? queryableMethod.GetGenericArguments() @@ -1409,21 +1376,22 @@ private NavigationExpansionExpression CreateNavigationExpansionExpression(Expres return new NavigationExpansionExpression(sourceExpression, currentTree, currentTree, parameterName); } - private Expression ExpandNavigationsInLambdaExpression(NavigationExpansionExpression source, LambdaExpression lambdaExpression) + private Expression ExpandNavigationsForSource(NavigationExpansionExpression source, Expression expression) { - var lambdaBody = ReplacingExpressionVisitor.Replace( - lambdaExpression.Parameters[0], - source.PendingSelector, - lambdaExpression.Body); + expression = new ExpandingExpressionVisitor(this, source).Visit(expression); + expression = _subqueryMemberPushdownExpressionVisitor.Visit(expression); + expression = Visit(expression); + expression = _pendingSelectorExpandingExpressionVisitor.Visit(expression); - lambdaBody = new ExpandingExpressionVisitor(this, source).Visit(lambdaBody); - lambdaBody = _subqueryMemberPushdownExpressionVisitor.Visit(lambdaBody); - lambdaBody = Visit(lambdaBody); - lambdaBody = _pendingSelectorExpandingExpressionVisitor.Visit(lambdaBody); - - return lambdaBody; + return expression; } + private Expression RemapLambdaExpression(NavigationExpansionExpression source, LambdaExpression lambdaExpression) + => ReplacingExpressionVisitor.Replace(lambdaExpression.Parameters[0], source.PendingSelector, lambdaExpression.Body); + + private LambdaExpression ProcessLambdaExpression(NavigationExpansionExpression source, LambdaExpression lambdaExpression) + => GenerateLambda(ExpandNavigationsForSource(source, RemapLambdaExpression(source, lambdaExpression)), source.CurrentParameter); + private static IEnumerable FindNavigations(IEntityType entityType, string navigationName) { var navigation = entityType.FindNavigation(navigationName); @@ -1480,18 +1448,7 @@ private IncludeTreeNode PopulateIncludeTree(IncludeTreeNode includeTreeNode, Exp return includeTreeNode; case MemberExpression memberExpression: - - var innerExpression = memberExpression.Expression; - Type convertedType = null; - if (innerExpression is UnaryExpression unaryExpression - && (unaryExpression.NodeType == ExpressionType.Convert - || unaryExpression.NodeType == ExpressionType.ConvertChecked - || unaryExpression.NodeType == ExpressionType.TypeAs)) - { - convertedType = unaryExpression.Type; - innerExpression = unaryExpression.Operand; - } - + var innerExpression = memberExpression.Expression.UnwrapTypeConversion(out var convertedType); var innerIncludeTreeNode = PopulateIncludeTree(includeTreeNode, innerExpression); var entityType = innerIncludeTreeNode.EntityType; if (convertedType != null) @@ -1499,15 +1456,15 @@ private IncludeTreeNode PopulateIncludeTree(IncludeTreeNode includeTreeNode, Exp entityType = entityType.GetTypesInHierarchy().FirstOrDefault(et => et.ClrType == convertedType); if (entityType == null) { - throw new InvalidOperationException("Invalid include."); + throw new InvalidOperationException("Invalid type conversion when specifying include."); } } var navigation = entityType.FindNavigation(memberExpression.Member); if (navigation != null) { - // This is to add eager Loaded navigations when owner type is included. var addedNode = innerIncludeTreeNode.AddNavigation(navigation); + // This is to add eager Loaded navigations when owner type is included. PopulateEagerLoadedNavigations(addedNode); return addedNode; } diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs index f53fbf7c457..f98af93a512 100644 --- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs @@ -56,13 +56,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp ShapedQueryExpression CheckTranslated(ShapedQueryExpression translated) { - if (translated == null) - { - throw new InvalidOperationException( - CoreStrings.TranslationFailed(methodCallExpression.Print())); - } - - return translated; + return translated ?? throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCallExpression.Print())); } var method = methodCallExpression.Method; diff --git a/src/Shared/EnumerableMethods.cs b/src/Shared/EnumerableMethods.cs index 6f5caac9606..73993e10347 100644 --- a/src/Shared/EnumerableMethods.cs +++ b/src/Shared/EnumerableMethods.cs @@ -10,6 +10,7 @@ namespace Microsoft.EntityFrameworkCore { internal static class EnumerableMethods { + public static MethodInfo AsEnumerable { get; } public static MethodInfo Cast { get; } public static MethodInfo OfType { get; } @@ -130,6 +131,8 @@ static EnumerableMethods() .GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) .ToList(); + AsEnumerable = enumerableMethods.Single( + mi => mi.Name == nameof(Enumerable.AsEnumerable) && mi.IsGenericMethod && mi.GetParameters().Length == 1); Cast = enumerableMethods.Single( mi => mi.Name == nameof(Enumerable.Cast) && mi.GetParameters().Length == 1); OfType = enumerableMethods.Single( diff --git a/src/Shared/ExpressionExtensions.cs b/src/Shared/ExpressionExtensions.cs index a8b3197b0e4..ec26daeb32f 100644 --- a/src/Shared/ExpressionExtensions.cs +++ b/src/Shared/ExpressionExtensions.cs @@ -18,7 +18,9 @@ public static Expression UnwrapTypeConversion(this Expression expression, out Ty { convertedType = null; while (expression is UnaryExpression unaryExpression - && unaryExpression.NodeType == ExpressionType.Convert) + && (unaryExpression.NodeType == ExpressionType.Convert + || unaryExpression.NodeType == ExpressionType.ConvertChecked + || unaryExpression.NodeType == ExpressionType.TypeAs)) { expression = unaryExpression.Operand; if (unaryExpression.Type != typeof(object) // Ignore object conversion diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs index ba8801cb58a..d47e6f75c2b 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs @@ -3988,6 +3988,57 @@ public override Task Subquery_DefaultIfEmpty_Any(bool async) return base.Subquery_DefaultIfEmpty_Any(async); } + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Projection_skip_collection_projection(bool async) + { + return base.Projection_skip_collection_projection(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Projection_take_collection_projection(bool async) + { + return base.Projection_take_collection_projection(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Projection_skip_take_collection_projection(bool async) + { + return base.Projection_skip_take_collection_projection(async); + } + + public override Task Projection_skip_projection(bool async) + { + return AssertTranslationFailed(() => base.Projection_skip_projection(async)); + } + + public override Task Projection_take_projection(bool async) + { + return AssertTranslationFailed(() => base.Projection_take_projection(async)); + } + + public override Task Projection_skip_take_projection(bool async) + { + return AssertTranslationFailed(() => base.Projection_skip_take_projection(async)); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Collection_projection_skip(bool async) + { + return base.Collection_projection_skip(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Collection_projection_take(bool async) + { + return base.Collection_projection_take(async); + } + + [ConditionalTheory(Skip = "Issue #17246")] + public override Task Collection_projection_skip_take(bool async) + { + return base.Collection_projection_skip_take(async); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs index 66eaa926e6e..8b409af6ccb 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs @@ -236,6 +236,18 @@ public override Task No_ignored_include_warning_when_implicit_load(bool async) return base.No_ignored_include_warning_when_implicit_load(async); } + [ConditionalTheory(Skip = "Skip withouth Take #18923")] + public override Task Client_method_skip_loads_owned_navigations(bool async) + { + return base.Client_method_skip_loads_owned_navigations(async); + } + + [ConditionalTheory(Skip = "Skip withouth Take #18923")] + public override Task Client_method_skip_loads_owned_navigations_variation_2(bool async) + { + return base.Client_method_skip_loads_owned_navigations_variation_2(async); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs index 8f88da66411..6c10a8500b6 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs @@ -103,59 +103,34 @@ public virtual void Throws_when_subquery_main_from_clause() public virtual void Throws_when_select_many() { using var context = CreateContext(); - Assert.Equal( - CoreStrings.QueryFailed( - "c1 => int[] { 1, 2, 3, }", - "NavigationExpandingExpressionVisitor"), - Assert.Throws( - () => (from c1 in context.Customers - from i in new[] { 1, 2, 3 } - select c1) - .ToList()).Message); + + AssertTranslationFailed( + () => (from c1 in context.Customers + from i in new[] { 1, 2, 3 } + select c1) + .ToList()); } [ConditionalFact] public virtual void Throws_when_join() { using var context = CreateContext(); - var message = Assert.Throws( + AssertTranslationFailed( () => (from e1 in context.Employees join i in new uint[] { 1, 2, 3 } on e1.EmployeeID equals i select e1) - .ToList()).Message; - - Assert.Equal( - CoreStrings.QueryFailed( - @"DbSet - .Join( - inner: __p_0, - outerKeySelector: e1 => e1.EmployeeID, - innerKeySelector: i => i, - resultSelector: (e1, i) => e1)", - "NavigationExpandingExpressionVisitor"), - message, ignoreLineEndingDifferences: true); + .ToList()); } [ConditionalFact] public virtual void Throws_when_group_join() { using var context = CreateContext(); - var message = Assert.Throws( + AssertTranslationFailed( () => (from e1 in context.Employees join i in new uint[] { 1, 2, 3 } on e1.EmployeeID equals i into g select e1) - .ToList()).Message; - - Assert.Equal( - CoreStrings.QueryFailed( - @"DbSet - .GroupJoin( - inner: __p_0, - outerKeySelector: e1 => e1.EmployeeID, - innerKeySelector: i => i, - resultSelector: (e1, g) => e1)", - "NavigationExpandingExpressionVisitor"), - message, ignoreLineEndingDifferences: true); + .ToList()); } [ConditionalFact(Skip = "Issue#18923")] diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index bfe6dcbe9a9..ec885ef1b58 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -1559,7 +1559,7 @@ public virtual async Task Select_navigation_with_concat_and_count(bool async) ss => ss.Set().Where(g => !g.HasSoulPatch).Select(g => g.Weapons.Concat(g.Weapons).Count())))).Message; Assert.Equal( - CoreStrings.QueryFailed( + CoreStrings.TranslationFailed( @"(MaterializeCollectionNavigation( navigation: Navigation: Gear.Weapons, subquery: (NavigationExpansionExpression @@ -1588,7 +1588,7 @@ public virtual async Task Select_navigation_with_concat_and_count(bool async) Value: (EntityReference: Gear) Expression: g), ""FullName"") != null && EF.Property((NavigationTreeExpression Value: (EntityReference: Gear) - Expression: g), ""FullName"") == EF.Property(i, ""OwnerFullName""))))", "NavigationExpandingExpressionVisitor"), + Expression: g), ""FullName"") == EF.Property(i, ""OwnerFullName""))))"), message, ignoreLineEndingDifferences: true); } @@ -1602,7 +1602,7 @@ public virtual async Task Concat_with_collection_navigations(bool async) ss => ss.Set().Where(g => g.HasSoulPatch).Select(g => g.Weapons.Union(g.Weapons).Count())))).Message; Assert.Equal( - CoreStrings.QueryFailed( + CoreStrings.TranslationFailed( @"(MaterializeCollectionNavigation( navigation: Navigation: Gear.Weapons, subquery: (NavigationExpansionExpression @@ -1631,7 +1631,7 @@ public virtual async Task Concat_with_collection_navigations(bool async) Value: (EntityReference: Gear) Expression: g), ""FullName"") != null && EF.Property((NavigationTreeExpression Value: (EntityReference: Gear) - Expression: g), ""FullName"") == EF.Property(i, ""OwnerFullName""))))", "NavigationExpandingExpressionVisitor"), + Expression: g), ""FullName"") == EF.Property(i, ""OwnerFullName""))))"), message, ignoreLineEndingDifferences: true); } @@ -3106,19 +3106,15 @@ orderby FavoriteWeapon(g.Weapons).Name descending [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual async Task Client_method_on_collection_navigation_in_additional_from_clause(bool async) + public virtual Task Client_method_on_collection_navigation_in_additional_from_clause(bool async) { - var message = (await Assert.ThrowsAsync( + return AssertTranslationFailed( () => AssertQuery( async, ss => from g in ss.Set().OfType() from v in Veterans(g.Reports) select new { g = g.Nickname, v = v.Nickname }, - elementSorter: e => e.g + e.v))).Message; - - Assert.StartsWith( - CoreStrings.QueryFailed("", "").Substring(0, 35), - message); + elementSorter: e => e.g + e.v)); } [ConditionalTheory(Skip = "Issue #17328")] @@ -7397,7 +7393,7 @@ public virtual Task OrderBy_bool_coming_from_optional_navigation(bool async) ss => ss.Set().Select(w => w.SynergyWith).OrderBy(g => MaybeScalar(g, () => g.IsAutomatic)), assertOrder: true); } - + [ConditionalFact] public virtual void Byte_array_filter_by_length_parameter_compiled() { diff --git a/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs index b193e5b7876..eb571ec4aca 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs @@ -3518,6 +3518,80 @@ orderby e.EmployeeID private static string ClientMethod(Employee e) => e.FirstName + " reports to " + e.Manager.FirstName + e.Manager.LastName; + // Issue#18672 + [ConditionalTheory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + public virtual async Task Multi_level_includes_are_applied_with_skip(bool useString, bool async) + { + using var context = CreateContext(); + var query = (from c in (useString + ? context.Customers.Include("Orders.OrderDetails") + : context.Customers.Include(e => e.Orders).ThenInclude(e => e.OrderDetails)) + where c.CustomerID.StartsWith("A") + orderby c.CustomerID + select new { c.CustomerID, Orders = c.Orders.ToList() }) + .Skip(1); + + var result = async + ? await query.FirstAsync() + : query.First(); + + Assert.Equal("ANATR", result.CustomerID); + Assert.Equal(2, result.Orders.First().OrderDetails.Count); + } + + [ConditionalTheory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + public virtual async Task Multi_level_includes_are_applied_with_take(bool useString, bool async) + { + using var context = CreateContext(); + var query = (from c in (useString + ? context.Customers.Include("Orders.OrderDetails") + : context.Customers.Include(e => e.Orders).ThenInclude(e => e.OrderDetails)) + where c.CustomerID.StartsWith("A") + orderby c.CustomerID + select new { c.CustomerID, Orders = c.Orders.ToList() }) + .Take(1); + + var result = async + ? await query.FirstAsync() + : query.First(); + + Assert.Equal("ALFKI", result.CustomerID); + Assert.Equal(3, result.Orders.First().OrderDetails.Count); + } + + [ConditionalTheory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + public virtual async Task Multi_level_includes_are_applied_with_skip_take(bool useString, bool async) + { + using var context = CreateContext(); + var query = (from c in (useString + ? context.Customers.Include("Orders.OrderDetails") + : context.Customers.Include(e => e.Orders).ThenInclude(e => e.OrderDetails)) + where c.CustomerID.StartsWith("A") + orderby c.CustomerID + select new { c.CustomerID, Orders = c.Orders.ToList() }) + .Skip(1) + .Take(1); + + var result = async + ? await query.FirstAsync() + : query.First(); + + Assert.Equal("ANATR", result.CustomerID); + Assert.Equal(2, result.Orders.First().OrderDetails.Count); + } + private static void CheckIsLoaded( NorthwindContext context, Customer customer, diff --git a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs index 27e983bd5c9..a10ae0ad3b9 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs @@ -1993,24 +1993,22 @@ public virtual Task OrderBy_scalar_primitive(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual async Task SelectMany_mixed(bool async) + public virtual Task SelectMany_mixed(bool async) { - Assert.Equal( - CoreStrings.QueryFailed("e1 => string[] { \"a\", \"b\", }", "NavigationExpandingExpressionVisitor"), - (await Assert.ThrowsAsync( - () => AssertQuery( - async, - ss => from e1 in ss.Set().OrderBy(e => e.EmployeeID).Take(2) - from s in new[] { "a", "b" } - from c in ss.Set().OrderBy(c => c.CustomerID).Take(2) - select new - { - e1, - s, - c - }, - e => (e.e1.EmployeeID, e.c.CustomerID), - entryCount: 4))).Message); + return AssertTranslationFailed( + () => AssertQuery( + async, + ss => from e1 in ss.Set().OrderBy(e => e.EmployeeID).Take(2) + from s in new[] { "a", "b" } + from c in ss.Set().OrderBy(c => c.CustomerID).Take(2) + select new + { + e1, + s, + c + }, + e => (e.e1.EmployeeID, e.c.CustomerID), + entryCount: 4)); } [ConditionalTheory] @@ -2425,41 +2423,26 @@ public virtual Task Default_if_empty_top_level_followed_by_projecting_constant(b [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual async Task Default_if_empty_top_level_arg(bool async) + public virtual Task Default_if_empty_top_level_arg(bool async) { - var message = (await Assert.ThrowsAsync( + return AssertTranslationFailed( () => AssertQuery( async, ss => from e in ss.Set().Where(c => c.EmployeeID == NonExistentID).DefaultIfEmpty(new Employee()) select e, - entryCount: 1))).Message; + entryCount: 1)); - Assert.Equal( - CoreStrings.QueryFailed( - @"DbSet - .Where(c => c.EmployeeID == 4294967295) - .DefaultIfEmpty(__p_0)", - "NavigationExpandingExpressionVisitor"), - message, ignoreLineEndingDifferences: true); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual async Task Default_if_empty_top_level_arg_followed_by_projecting_constant(bool async) + public virtual Task Default_if_empty_top_level_arg_followed_by_projecting_constant(bool async) { - var message = (await Assert.ThrowsAsync( + return AssertTranslationFailed( () => AssertQueryScalar( async, ss => from e in ss.Set().Where(c => c.EmployeeID == NonExistentID).DefaultIfEmpty(new Employee()) - select 42))).Message; - - Assert.Equal( - CoreStrings.QueryFailed( - @"DbSet - .Where(c => c.EmployeeID == 4294967295) - .DefaultIfEmpty(__p_0)", - "NavigationExpandingExpressionVisitor"), - message, ignoreLineEndingDifferences: true); + select 42)); } [ConditionalTheory] @@ -3300,7 +3283,7 @@ public virtual void Select_Where_Subquery_Equality() using var context = CreateContext(); var orders = (from o in context.Orders.OrderBy(o => o.OrderID).Take(1) - // ReSharper disable once UseMethodAny.0 + // ReSharper disable once UseMethodAny.0 where (from od in context.OrderDetails.OrderBy(od => od.OrderID).Take(2) where (from c in context.Set() where c.CustomerID == o.CustomerID @@ -5489,5 +5472,184 @@ public virtual Task Subquery_DefaultIfEmpty_Any(bool async) .DefaultIfEmpty() select e)); } + + // Issue#18374 + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Projection_skip_collection_projection(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new { Item = o }) + .Skip(5) + .Select(e => new + { + e.Item.OrderID, + ProductIds = e.Item.OrderDetails.Select(od => od.ProductID).ToList() + }), + assertOrder: true, + elementAsserter: (e, a) => + { + Assert.Equal(e.OrderID, a.OrderID); + AssertCollection(e.ProductIds, a.ProductIds, ordered: true, elementAsserter: (ie, ia) => Assert.Equal(ie, ia)); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Projection_take_collection_projection(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new { Item = o }) + .Take(10) + .Select(e => new + { + e.Item.OrderID, + ProductIds = e.Item.OrderDetails.Select(od => od.ProductID).ToList() + }), + assertOrder: true, + elementAsserter: (e, a) => + { + Assert.Equal(e.OrderID, a.OrderID); + AssertCollection(e.ProductIds, a.ProductIds, ordered: true, elementAsserter: (ie, ia) => Assert.Equal(ie, ia)); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Projection_skip_take_collection_projection(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new { Item = o }) + .Skip(5) + .Take(10) + .Select(e => new + { + e.Item.OrderID, + ProductIds = e.Item.OrderDetails.Select(od => od.ProductID).ToList() + }), + assertOrder: true, + elementAsserter: (e, a) => + { + Assert.Equal(e.OrderID, a.OrderID); + AssertCollection(e.ProductIds, a.ProductIds, ordered: true, elementAsserter: (ie, ia) => Assert.Equal(ie, ia)); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Projection_skip_projection(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new { Item = o }) + .Skip(5) + .Select(e => new { e.Item.Customer.City })); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Projection_take_projection(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new { Item = o }) + .Take(10) + .Select(e => new { e.Item.Customer.City })); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Projection_skip_take_projection(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new { Item = o }) + .Skip(5) + .Take(10) + .Select(e => new { e.Item.Customer.City })); + } + + // Issue#19207 + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Collection_projection_skip(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new + { + Order = o, + o.OrderDetails + }) + .Skip(5), + entryCount: 173, + assertOrder: true, + elementAsserter: (e, a) => { AssertEqual(e.Order, a.Order); AssertCollection(e.OrderDetails, a.OrderDetails); }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Collection_projection_take(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new + { + Order = o, + o.OrderDetails + }) + .Take(10), + entryCount: 39, + assertOrder: true, + elementAsserter: (e, a) => { AssertEqual(e.Order, a.Order); AssertCollection(e.OrderDetails, a.OrderDetails); }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Collection_projection_skip_take(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(o => o.OrderID < 10300) + .OrderBy(o => o.OrderID) + .Select(o => new + { + Order = o, + o.OrderDetails + }) + .Skip(5) + .Take(10), + entryCount: 39, + assertOrder: true, + elementAsserter: (e, a) => { AssertEqual(e.Order, a.Order); AssertCollection(e.OrderDetails, a.OrderDetails); }); + } } } diff --git a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs index 46886e10f05..79002e88e65 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs @@ -1252,7 +1252,9 @@ public virtual Task Where_bool_client_side_negated(bool async) ss => ss.Set().Where(p => !ClientFunc(p.ProductID) && p.Discontinued), entryCount: 8)); } +#pragma warning disable IDE0060 // Remove unused parameter private static bool ClientFunc(int id) +#pragma warning restore IDE0060 // Remove unused parameter { return false; } @@ -1994,5 +1996,96 @@ public virtual Task Using_same_parameter_twice_in_query_generates_one_sql_parame ss => ss.Set().Where(c => i + c.CustomerID + i == c.CompanyName) .Select(c => c.CustomerID)); } + + //[ConditionalTheory] + //[MemberData(nameof(IsAsyncData))] + //public virtual Task Where_ToList_Count(bool async) + //{ + // return AssertQuery( + // async, + // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToList()) + // .Where(e => e.Count() == 0), + // entryCount: 6); + //} + + //[ConditionalTheory] + //[MemberData(nameof(IsAsyncData))] + //public virtual Task Where_ToList_Contains(bool async) + //{ + // return AssertQuery( + // async, + // ss => ss.Set() + // .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).ToList()) + // .Where(e => e.Contains("ALFKI")), + // entryCount: 6); + //} + + //[ConditionalTheory] + //[MemberData(nameof(IsAsyncData))] + //public virtual Task Where_ToArray_Count(bool async) + //{ + // return AssertQuery( + // async, + // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToArray()) + // .Where(e => e.Count() == 0), + // entryCount: 6); + //} + + //[ConditionalTheory] + //[MemberData(nameof(IsAsyncData))] + //public virtual Task Where_ToArray_Contains(bool async) + //{ + // return AssertQuery( + // async, + // ss => ss.Set() + // .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).ToArray()) + // .Where(e => e.Contains("ALFKI")), + // entryCount: 6); + //} + + //[ConditionalTheory] + //[MemberData(nameof(IsAsyncData))] + //public virtual Task Where_AsEnumerable_Count(bool async) + //{ + // return AssertQuery( + // async, + // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).AsEnumerable()) + // .Where(e => e.Count() == 0), + // entryCount: 6); + //} + + //[ConditionalTheory] + //[MemberData(nameof(IsAsyncData))] + //public virtual Task Where_AsEnumerable_Contains(bool async) + //{ + // return AssertQuery( + // async, + // ss => ss.Set() + // .Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).Select(o => o.CustomerID).AsEnumerable()) + // .Where(e => e.Contains("ALFKI")), + // entryCount: 6); + //} + + //[ConditionalTheory] + //[MemberData(nameof(IsAsyncData))] + //public virtual Task Where_ToList_Count_member(bool async) + //{ + // return AssertQuery( + // async, + // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToList()) + // .Where(e => e.Count == 0), + // entryCount: 6); + //} + + //[ConditionalTheory] + //[MemberData(nameof(IsAsyncData))] + //public virtual Task Where_ToArray_Length_member(bool async) + //{ + // return AssertQuery( + // async, + // ss => ss.Set().Select(c => ss.Set().Where(o => o.CustomerID == c.CustomerID).ToArray()) + // .Where(e => e.Length == 0), + // entryCount: 6); + //} } } diff --git a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs index e7b8ecddfb9..2ab142108e1 100644 --- a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs @@ -418,6 +418,68 @@ public virtual Task Unmapped_property_projection_loads_owned_navigations(bool as entryCount: 5); } + // Issue#18140 + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Client_method_skip_loads_owned_navigations(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(e => e.Id).Select(e => Map(e)).Skip(1)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Client_method_take_loads_owned_navigations(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(e => e.Id).Select(e => Map(e)).Take(2)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Client_method_skip_take_loads_owned_navigations(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(e => e.Id).Select(e => Map(e)).Skip(1).Take(2)); + } + + private static string Map(OwnedPerson person) => person.PersonAddress.Country.Name; + + // Issue#18734 + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Client_method_skip_loads_owned_navigations_variation_2(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(e => e.Id).Select(e => Identity(e)).Skip(1)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Client_method_take_loads_owned_navigations_variation_2(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(e => e.Id).Select(e => Identity(e)).Take(2)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Client_method_skip_take_loads_owned_navigations_variation_2(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(e => e.Id).Select(e => Identity(e)).Skip(1).Take(2)); + } + + private static OwnedPerson Identity(OwnedPerson person) => person; + + + protected virtual DbContext CreateContext() => Fixture.CreateContext(); public abstract class OwnedQueryFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index 525853ef2e3..ae2be9faa87 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -1477,13 +1477,13 @@ public override async Task Order_by_key_of_projected_navigation_doesnt_get_optim SELECT [l1].[Name] FROM ( - SELECT TOP(@__p_0) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l].[Id] AS [Id0] + SELECT TOP(@__p_0) [l].[Id], [l].[Level2_Optional_Id], [l].[Level2_Required_Id], [l].[Name], [l].[OneToMany_Optional_Inverse3Id], [l].[OneToMany_Optional_Self_Inverse3Id], [l].[OneToMany_Required_Inverse3Id], [l].[OneToMany_Required_Self_Inverse3Id], [l].[OneToOne_Optional_PK_Inverse3Id], [l].[OneToOne_Optional_Self3Id], [l0].[Id] AS [Id0], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelThree] AS [l] INNER JOIN [LevelTwo] AS [l0] ON [l].[Level2_Required_Id] = [l0].[Id] ORDER BY [l0].[Id] ) AS [t] INNER JOIN [LevelOne] AS [l1] ON [t].[Level1_Required_Id] = [l1].[Id] -ORDER BY [t].[Id]"); +ORDER BY [t].[Id0]"); } public override async Task Order_by_key_of_anonymous_type_projected_navigation_doesnt_get_optimized_into_FK_access_subquery( @@ -1509,13 +1509,13 @@ public override async Task Optional_navigation_take_optional_navigation(bool asy SELECT [l1].[Name] FROM ( - SELECT TOP(@__p_0) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l].[Id] AS [Id0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] ORDER BY [l0].[Id] ) AS [t] -LEFT JOIN [LevelThree] AS [l1] ON [t].[Id] = [l1].[Level2_Optional_Id] -ORDER BY [t].[Id]"); +LEFT JOIN [LevelThree] AS [l1] ON [t].[Id0] = [l1].[Level2_Optional_Id] +ORDER BY [t].[Id0]"); } public override async Task Projection_select_correct_table_from_subquery_when_materialization_is_not_required(bool async) @@ -2383,7 +2383,7 @@ public override async Task GroupJoin_on_a_subquery_containing_another_GroupJoin_ SELECT [l1].[Name] FROM ( - SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] ORDER BY [l].[Id] @@ -2417,13 +2417,13 @@ public override async Task GroupJoin_on_a_subquery_containing_another_GroupJoin_ SELECT [l1].[Name] FROM ( - SELECT TOP(@__p_0) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l].[Id] AS [Id0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] ORDER BY [l].[Id] ) AS [t] LEFT JOIN [LevelOne] AS [l1] ON [t].[Level1_Optional_Id] = [l1].[Id] -ORDER BY [t].[Id0]"); +ORDER BY [t].[Id]"); } public override async Task GroupJoin_on_a_subquery_containing_another_GroupJoin_with_orderby_on_inner_sequence_projecting_inner( @@ -2436,7 +2436,7 @@ public override async Task GroupJoin_on_a_subquery_containing_another_GroupJoin_ SELECT [l1].[Name] FROM ( - SELECT TOP(@__p_0) [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [l].[Id] AS [Id0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id] AS [Id0], [t].[Date] AS [Date0], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name] AS [Name0], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN ( SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] @@ -2445,7 +2445,7 @@ FROM [LevelTwo] AS [l0] ORDER BY [l].[Id] ) AS [t0] LEFT JOIN [LevelOne] AS [l1] ON [t0].[Level1_Optional_Id] = [l1].[Id] -ORDER BY [t0].[Id0]"); +ORDER BY [t0].[Id]"); } public override async Task GroupJoin_on_left_side_being_a_subquery(bool async) @@ -2455,15 +2455,10 @@ public override async Task GroupJoin_on_left_side_being_a_subquery(bool async) AssertSql( @"@__p_0='2' -SELECT [t].[Id], [l1].[Name] AS [Brand] -FROM ( - SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Name] AS [Name0] - FROM [LevelOne] AS [l] - LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] - ORDER BY [l0].[Name], [l].[Id] -) AS [t] -LEFT JOIN [LevelTwo] AS [l1] ON [t].[Id] = [l1].[Level1_Optional_Id] -ORDER BY [t].[Name0], [t].[Id]"); +SELECT TOP(@__p_0) [l].[Id], [l0].[Name] AS [Brand] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] +ORDER BY [l0].[Name], [l].[Id]"); } public override async Task GroupJoin_on_right_side_being_a_subquery(bool async) @@ -2476,7 +2471,7 @@ public override async Task GroupJoin_on_right_side_being_a_subquery(bool async) SELECT [l].[Id], [t].[Name] FROM [LevelTwo] AS [l] LEFT JOIN ( - SELECT TOP(@__p_0) [l0].[Id], [l0].[Date], [l0].[Name], [l0].[OneToMany_Optional_Self_Inverse1Id], [l0].[OneToMany_Required_Self_Inverse1Id], [l0].[OneToOne_Optional_Self1Id], [l1].[Name] AS [Name0] + SELECT TOP(@__p_0) [l0].[Id], [l0].[Date], [l0].[Name], [l0].[OneToMany_Optional_Self_Inverse1Id], [l0].[OneToMany_Required_Self_Inverse1Id], [l0].[OneToOne_Optional_Self1Id], [l1].[Id] AS [Id0], [l1].[Date] AS [Date0], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name] AS [Name0], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l0] LEFT JOIN [LevelTwo] AS [l1] ON [l0].[Id] = [l1].[Level1_Optional_Id] ORDER BY [l1].[Name] @@ -2757,13 +2752,13 @@ public override async Task Nested_group_join_with_take(bool async) SELECT [l1].[Name] FROM ( - SELECT TOP(@__p_0) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l].[Id] AS [Id0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] ORDER BY [l].[Id] ) AS [t] -LEFT JOIN [LevelTwo] AS [l1] ON [t].[Id] = [l1].[Level1_Optional_Id] -ORDER BY [t].[Id0]"); +LEFT JOIN [LevelTwo] AS [l1] ON [t].[Id0] = [l1].[Level1_Optional_Id] +ORDER BY [t].[Id]"); } public override async Task Navigation_with_same_navigation_compared_to_null(bool async) @@ -3692,7 +3687,7 @@ public override async Task Include18_1_1(bool async) SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id] FROM ( - SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Name] AS [Name0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] ORDER BY [l0].[Name] @@ -3724,15 +3719,14 @@ public override void Include18_3() AssertSql( @"@__p_0='10' -SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id] +SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id] FROM ( - SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Name] AS [Name0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] ORDER BY [l0].[Name] ) AS [t] LEFT JOIN [LevelTwo] AS [l1] ON [t].[Id] = [l1].[Level1_Optional_Id] -LEFT JOIN [LevelTwo] AS [l2] ON [t].[Id] = [l2].[Level1_Optional_Id] ORDER BY [t].[Name0]"); } @@ -3744,15 +3738,14 @@ public override void Include18_3_1() AssertSql( @"@__p_0='10' -SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id] +SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id] FROM ( - SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Name] AS [Name0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] ORDER BY [l0].[Name] ) AS [t] LEFT JOIN [LevelTwo] AS [l1] ON [t].[Id] = [l1].[Level1_Optional_Id] -LEFT JOIN [LevelTwo] AS [l2] ON [t].[Id] = [l2].[Level1_Optional_Id] ORDER BY [t].[Name0]"); } @@ -3764,15 +3757,14 @@ public override void Include18_3_2() AssertSql( @"@__p_0='10' -SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id] +SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id] FROM ( - SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Name] AS [Name0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] ORDER BY [l0].[Name] ) AS [t] LEFT JOIN [LevelTwo] AS [l1] ON [t].[Id] = [l1].[Level1_Optional_Id] -LEFT JOIN [LevelTwo] AS [l2] ON [t].[Id] = [l2].[Level1_Optional_Id] ORDER BY [t].[Name0]"); } @@ -3812,13 +3804,13 @@ public override void Include18() AssertSql( @"@__p_0='10' -SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Id0], [t].[Date0], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name0], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id] +SELECT [t].[Id], [t].[Date], [t].[Name], [t].[OneToMany_Optional_Self_Inverse1Id], [t].[OneToMany_Required_Self_Inverse1Id], [t].[OneToOne_Optional_Self1Id], [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] FROM ( - SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id] AS [Id0], [l0].[Date] AS [Date0], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name] AS [Name0], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id] FROM [LevelOne] AS [l] - LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[OneToOne_Optional_PK_Inverse2Id] ORDER BY [l].[Id] ) AS [t] +LEFT JOIN [LevelTwo] AS [l0] ON [t].[Id] = [l0].[OneToOne_Optional_PK_Inverse2Id] LEFT JOIN [LevelTwo] AS [l1] ON [t].[Id] = [l1].[Level1_Optional_Id] ORDER BY [t].[Id]"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs index ff52f89cb4a..01007af18aa 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs @@ -172,7 +172,7 @@ public override async Task Nested_group_join_with_take(bool async) SELECT [t5].[Level2_Name] FROM ( - SELECT TOP(@__p_0) [t1].[Id], [t1].[OneToOne_Required_PK_Date], [t1].[Level1_Optional_Id], [t1].[Level1_Required_Id], [t1].[Level2_Name], [t1].[OneToMany_Optional_Inverse2Id], [t1].[OneToMany_Required_Inverse2Id], [t1].[OneToOne_Optional_PK_Inverse2Id], [l].[Id] AS [Id0] + SELECT TOP(@__p_0) [l].[Id], [l].[Date], [l].[Name], [t0].[Id] AS [Id0], [t0].[Date] AS [Date0], [t0].[Name] AS [Name0] FROM [Level1] AS [l] LEFT JOIN ( SELECT [l0].[Id], [l0].[Date], [l0].[Name], [t].[Id] AS [Id0], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Level2_Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id] @@ -185,14 +185,14 @@ WHERE [l1].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l1].[Level1_Require ) AS [t] ON [l0].[Id] = [t].[Id] WHERE [t].[Id] IS NOT NULL ) AS [t0] ON [l].[Id] = [t0].[Level1_Optional_Id] - LEFT JOIN ( - SELECT [l3].[Id], [l3].[OneToOne_Required_PK_Date], [l3].[Level1_Optional_Id], [l3].[Level1_Required_Id], [l3].[Level2_Name], [l3].[OneToMany_Optional_Inverse2Id], [l3].[OneToMany_Required_Inverse2Id], [l3].[OneToOne_Optional_PK_Inverse2Id], [l4].[Id] AS [Id0] - FROM [Level1] AS [l3] - INNER JOIN [Level1] AS [l4] ON [l3].[Id] = [l4].[Id] - WHERE [l3].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l3].[Level1_Required_Id] IS NOT NULL AND [l3].[OneToOne_Required_PK_Date] IS NOT NULL) - ) AS [t1] ON [t0].[Id] = [t1].[Id] ORDER BY [l].[Id] -) AS [t2] +) AS [t1] +LEFT JOIN ( + SELECT [l3].[Id], [l3].[OneToOne_Required_PK_Date], [l3].[Level1_Optional_Id], [l3].[Level1_Required_Id], [l3].[Level2_Name], [l3].[OneToMany_Optional_Inverse2Id], [l3].[OneToMany_Required_Inverse2Id], [l3].[OneToOne_Optional_PK_Inverse2Id], [l4].[Id] AS [Id0] + FROM [Level1] AS [l3] + INNER JOIN [Level1] AS [l4] ON [l3].[Id] = [l4].[Id] + WHERE [l3].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l3].[Level1_Required_Id] IS NOT NULL AND [l3].[OneToOne_Required_PK_Date] IS NOT NULL) +) AS [t2] ON [t1].[Id0] = [t2].[Id] LEFT JOIN ( SELECT [l5].[Id], [l5].[Date], [l5].[Name], [t3].[Id] AS [Id0], [t3].[OneToOne_Required_PK_Date], [t3].[Level1_Optional_Id], [t3].[Level1_Required_Id], [t3].[Level2_Name], [t3].[OneToMany_Optional_Inverse2Id], [t3].[OneToMany_Required_Inverse2Id], [t3].[OneToOne_Optional_PK_Inverse2Id] FROM [Level1] AS [l5] @@ -210,7 +210,7 @@ FROM [Level1] AS [l8] INNER JOIN [Level1] AS [l9] ON [l8].[Id] = [l9].[Id] WHERE [l8].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l8].[Level1_Required_Id] IS NOT NULL AND [l8].[OneToOne_Required_PK_Date] IS NOT NULL) ) AS [t5] ON [t4].[Id] = [t5].[Id] -ORDER BY [t2].[Id0]"); +ORDER BY [t1].[Id]"); } public override async Task Explicit_GroupJoin_in_subquery_with_unrelated_projection2(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 24d0f6fec0f..7e94dd15120 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -4990,9 +4990,9 @@ public override async Task Include_on_derived_type_with_order_by_and_paging(bool AssertSql( @"@__p_0='10' -SELECT [t1].[Name], [t1].[Discriminator], [t1].[LocustHordeId], [t1].[ThreatLevel], [t1].[DefeatedByNickname], [t1].[DefeatedBySquadId], [t1].[HighCommandId], [t2].[Nickname], [t2].[SquadId], [t2].[AssignedCityName], [t2].[CityOfBirthName], [t2].[Discriminator], [t2].[FullName], [t2].[HasSoulPatch], [t2].[LeaderNickname], [t2].[LeaderSquadId], [t2].[Rank], [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] +SELECT [t1].[Name], [t1].[Discriminator], [t1].[LocustHordeId], [t1].[ThreatLevel], [t1].[DefeatedByNickname], [t1].[DefeatedBySquadId], [t1].[HighCommandId], [t1].[Nickname], [t1].[SquadId], [t1].[AssignedCityName], [t1].[CityOfBirthName], [t1].[Discriminator0], [t1].[FullName], [t1].[HasSoulPatch], [t1].[LeaderNickname], [t1].[LeaderSquadId], [t1].[Rank], [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM ( - SELECT TOP(@__p_0) [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId], [t0].[Note] + SELECT TOP(@__p_0) [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[Discriminator] AS [Discriminator0], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t0].[Note] FROM [LocustLeaders] AS [l] LEFT JOIN ( SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] @@ -5003,12 +5003,7 @@ LEFT JOIN [Tags] AS [t0] ON (([t].[Nickname] = [t0].[GearNickName]) OR ([t].[Nic WHERE [l].[Discriminator] IN (N'LocustLeader', N'LocustCommander') ORDER BY [t0].[Note] ) AS [t1] -LEFT JOIN ( - SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank] - FROM [Gears] AS [g0] - WHERE [g0].[Discriminator] IN (N'Gear', N'Officer') -) AS [t2] ON ([t1].[DefeatedByNickname] = [t2].[Nickname]) AND ([t1].[DefeatedBySquadId] = [t2].[SquadId]) -LEFT JOIN [Weapons] AS [w] ON [t2].[FullName] = [w].[OwnerFullName] +LEFT JOIN [Weapons] AS [w] ON [t1].[FullName] = [w].[OwnerFullName] ORDER BY [t1].[Note], [t1].[Name], [w].[Id]"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs index 6c183748b4b..638f79e2770 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs @@ -1188,6 +1188,82 @@ FROM [Employees] AS [e] ORDER BY [e].[EmployeeID]"); } + public override async Task Multi_level_includes_are_applied_with_skip(bool useString, bool async) + { + await base.Multi_level_includes_are_applied_with_skip(useString, async); + + AssertSql( + @"@__p_0='1' + +SELECT [t].[CustomerID], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[OrderID0], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice] +FROM ( + SELECT [c].[CustomerID] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY [c].[CustomerID] + OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY +) AS [t] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Orders] AS [o] + LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t0] ON [t].[CustomerID] = [t0].[CustomerID] +ORDER BY [t].[CustomerID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]"); + } + + public override async Task Multi_level_includes_are_applied_with_take(bool useString, bool async) + { + await base.Multi_level_includes_are_applied_with_take(useString, async); + + AssertSql( + @"@__p_0='1' + +SELECT [t0].[CustomerID], [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID], [t1].[Discount], [t1].[Quantity], [t1].[UnitPrice] +FROM ( + SELECT TOP(1) [t].[CustomerID] + FROM ( + SELECT TOP(@__p_0) [c].[CustomerID] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY [c].[CustomerID] + ) AS [t] + ORDER BY [t].[CustomerID] +) AS [t0] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Orders] AS [o] + LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t1] ON [t0].[CustomerID] = [t1].[CustomerID] +ORDER BY [t0].[CustomerID], [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID]"); + } + + public override async Task Multi_level_includes_are_applied_with_skip_take(bool useString, bool async) + { + await base.Multi_level_includes_are_applied_with_skip_take(useString, async); + + AssertSql( + @"@__p_0='1' + +SELECT [t0].[CustomerID], [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID], [t1].[Discount], [t1].[Quantity], [t1].[UnitPrice] +FROM ( + SELECT TOP(1) [t].[CustomerID] + FROM ( + SELECT [c].[CustomerID] + FROM [Customers] AS [c] + WHERE [c].[CustomerID] LIKE N'A%' + ORDER BY [c].[CustomerID] + OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY + ) AS [t] + ORDER BY [t].[CustomerID] +) AS [t0] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Orders] AS [o] + LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +) AS [t1] ON [t0].[CustomerID] = [t1].[CustomerID] +ORDER BY [t0].[CustomerID], [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs index 3e7d148376f..4658c8290be 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -2647,9 +2647,9 @@ public override async Task Select_take_skip_null_coalesce_operator(bool async) @"@__p_0='10' @__p_1='5' -SELECT [t].[CustomerID], [t].[CompanyName], [t].[c] AS [Region] +SELECT [t].[CustomerID], [t].[CompanyName], COALESCE([t].[Region], N'ZZ') AS [Region] FROM ( - SELECT TOP(@__p_0) [c].[CustomerID], [c].[CompanyName], COALESCE([c].[Region], N'ZZ') AS [c] + 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], COALESCE([c].[Region], N'ZZ') AS [c] FROM [Customers] AS [c] ORDER BY COALESCE([c].[Region], N'ZZ') ) AS [t] @@ -2668,7 +2668,7 @@ public override async Task Select_take_skip_null_coalesce_operator2(bool async) SELECT [t].[CustomerID], [t].[CompanyName], [t].[Region] FROM ( - SELECT TOP(@__p_0) [c].[CustomerID], [c].[CompanyName], [c].[Region], COALESCE([c].[Region], N'ZZ') AS [c] + 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], COALESCE([c].[Region], N'ZZ') AS [c] FROM [Customers] AS [c] ORDER BY COALESCE([c].[Region], N'ZZ') ) AS [t] @@ -4494,7 +4494,7 @@ public override async Task Join_take_count_works(bool async) SELECT COUNT(*) FROM ( - SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[CustomerID] AS [CustomerID0] + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[CustomerID] AS [CustomerID0], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region] FROM [Orders] AS [o] INNER JOIN ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] @@ -4728,6 +4728,177 @@ ELSE CAST(0 AS bit) END"); } + public override async Task Projection_skip_collection_projection(bool async) + { + await base.Projection_skip_collection_projection(async); + + AssertSql( + @"@__p_0='5' + +SELECT [t].[OrderID], [o0].[ProductID], [o0].[OrderID] +FROM ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] + OFFSET @__p_0 ROWS +) AS [t] +LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Projection_take_collection_projection(bool async) + { + await base.Projection_take_collection_projection(async); + + AssertSql( + @"@__p_0='10' + +SELECT [t].[OrderID], [o0].[ProductID], [o0].[OrderID] +FROM ( + SELECT TOP(@__p_0) [o].[OrderID] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] +) AS [t] +LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Projection_skip_take_collection_projection(bool async) + { + await base.Projection_skip_take_collection_projection(async); + + AssertSql( + @"@__p_0='5' +@__p_1='10' + +SELECT [t].[OrderID], [o0].[ProductID], [o0].[OrderID] +FROM ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t] +LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Projection_skip_projection(bool async) + { + await base.Projection_skip_projection(async); + + AssertSql( + @"@__p_0='5' + +SELECT [c].[City] +FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] + OFFSET @__p_0 ROWS +) AS [t] +LEFT JOIN [Customers] AS [c] ON [t].[CustomerID] = [c].[CustomerID] +ORDER BY [t].[OrderID]"); + } + + public override async Task Projection_take_projection(bool async) + { + await base.Projection_take_projection(async); + + AssertSql( + @"@__p_0='10' + +SELECT [c].[City] +FROM ( + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] +) AS [t] +LEFT JOIN [Customers] AS [c] ON [t].[CustomerID] = [c].[CustomerID] +ORDER BY [t].[OrderID]"); + } + + public override async Task Projection_skip_take_projection(bool async) + { + await base.Projection_skip_take_projection(async); + + AssertSql( + @"@__p_0='5' +@__p_1='10' + +SELECT [c].[City] +FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t] +LEFT JOIN [Customers] AS [c] ON [t].[CustomerID] = [c].[CustomerID] +ORDER BY [t].[OrderID]"); + } + + public override async Task Collection_projection_skip(bool async) + { + await base.Collection_projection_skip(async); + + AssertSql( + @"@__p_0='5' + +SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] + OFFSET @__p_0 ROWS +) AS [t] +LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Collection_projection_take(bool async) + { + await base.Collection_projection_take(async); + + AssertSql( + @"@__p_0='10' + +SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM ( + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] +) AS [t] +LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + + public override async Task Collection_projection_skip_take(bool async) + { + await base.Collection_projection_skip_take(async); + + AssertSql( + @"@__p_0='5' +@__p_1='10' + +SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +FROM ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 + ORDER BY [o].[OrderID] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t] +LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] +ORDER BY [t].[OrderID], [o0].[OrderID], [o0].[ProductID]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindNavigationsQuerySqlServerTest.cs index 267feffdcb5..512ba0b2d02 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindNavigationsQuerySqlServerTest.cs @@ -893,14 +893,18 @@ public override async Task Project_single_scalar_value_subquery_in_query_with_op AssertSql( @"@__p_0='3' -SELECT TOP(@__p_0) [o0].[OrderID], ( +SELECT [t].[OrderID], ( SELECT TOP(1) [o].[OrderID] FROM [Order Details] AS [o] - WHERE [o0].[OrderID] = [o].[OrderID] + WHERE [t].[OrderID] = [o].[OrderID] ORDER BY [o].[OrderID], [o].[ProductID]) AS [OrderDetail], [c].[City] -FROM [Orders] AS [o0] -LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] -ORDER BY [o0].[OrderID]"); +FROM ( + SELECT TOP(@__p_0) [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] + FROM [Orders] AS [o0] + ORDER BY [o0].[OrderID] +) AS [t] +LEFT JOIN [Customers] AS [c] ON [t].[CustomerID] = [c].[CustomerID] +ORDER BY [t].[OrderID]"); } public override async Task GroupJoin_with_complex_subquery_and_LOJ_gets_flattened(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs index 6efa8b4e010..dee4239c6b4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs @@ -1015,7 +1015,7 @@ public override async Task Where_primitive(bool async) SELECT [t].[EmployeeID] FROM ( - SELECT TOP(@__p_0) [e].[EmployeeID] + SELECT TOP(@__p_0) [e].[EmployeeID], [e].[City], [e].[Country], [e].[FirstName], [e].[ReportsTo], [e].[Title] FROM [Employees] AS [e] ) AS [t] WHERE [t].[EmployeeID] = 5"); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs index 68eef30073d..ca24011c036 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs @@ -1426,13 +1426,12 @@ public override async Task Preserve_includes_when_applying_skip_take_after_anony FROM [OwnedPerson] AS [o] WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA')", // - @"@__Count_0='4' -@__p_1='0' + @"@__p_1='0' @__p_2='100' -SELECT [t].[Id], [t].[Discriminator], [t3].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name], [t6].[PersonAddress_Country_PlanetId], [t8].[Id], [t11].[Id], [t11].[BranchAddress_Country_Name], [t11].[BranchAddress_Country_PlanetId], [t13].[Id], [t16].[Id], [t16].[LeafBAddress_Country_Name], [t16].[LeafBAddress_Country_PlanetId], [t18].[Id], [t21].[Id], [t21].[LeafAAddress_Country_Name], [t21].[LeafAAddress_Country_PlanetId], [t].[c], [o22].[ClientId], [o22].[Id] +SELECT [t].[Id], [t].[Discriminator], [t3].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name], [t6].[PersonAddress_Country_PlanetId], [t8].[Id], [t11].[Id], [t11].[BranchAddress_Country_Name], [t11].[BranchAddress_Country_PlanetId], [t13].[Id], [t16].[Id], [t16].[LeafBAddress_Country_Name], [t16].[LeafBAddress_Country_PlanetId], [t18].[Id], [t21].[Id], [t21].[LeafAAddress_Country_Name], [t21].[LeafAAddress_Country_PlanetId], [o22].[ClientId], [o22].[Id] FROM ( - SELECT [o].[Id], [o].[Discriminator], @__Count_0 AS [c] + SELECT [o].[Id], [o].[Discriminator] FROM [OwnedPerson] AS [o] WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') ORDER BY [o].[Id] @@ -1647,6 +1646,726 @@ WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') AND ORDER BY [o].[Id], [o20].[ClientId], [o20].[Id]"); } + public override async Task Client_method_skip_loads_owned_navigations(bool async) + { + await base.Client_method_skip_loads_owned_navigations(async); + + AssertSql( + @"@__p_0='1' + +SELECT [t].[Id], [t].[Discriminator], [t3].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name], [t6].[PersonAddress_Country_PlanetId], [t8].[Id], [t11].[Id], [t11].[BranchAddress_Country_Name], [t11].[BranchAddress_Country_PlanetId], [t13].[Id], [t16].[Id], [t16].[LeafBAddress_Country_Name], [t16].[LeafBAddress_Country_PlanetId], [t18].[Id], [t21].[Id], [t21].[LeafAAddress_Country_Name], [t21].[LeafAAddress_Country_PlanetId], [o22].[ClientId], [o22].[Id] +FROM ( + SELECT [o].[Id], [o].[Discriminator] + FROM [OwnedPerson] AS [o] + WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ORDER BY [o].[Id] + OFFSET @__p_0 ROWS +) AS [t] +LEFT JOIN ( + SELECT [o0].[Id], [t0].[Id] AS [Id0] + FROM [OwnedPerson] AS [o0] + INNER JOIN ( + SELECT [o1].[Id], [o1].[Discriminator] + FROM [OwnedPerson] AS [o1] + WHERE [o1].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t0] ON [o0].[Id] = [t0].[Id] +) AS [t1] ON [t].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [o2].[Id], [t2].[Id] AS [Id0] + FROM [OwnedPerson] AS [o2] + INNER JOIN ( + SELECT [o3].[Id], [o3].[Discriminator] + FROM [OwnedPerson] AS [o3] + WHERE [o3].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t2] ON [o2].[Id] = [t2].[Id] +) AS [t3] ON [t].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [o4].[Id], [o4].[PersonAddress_Country_Name], [o4].[PersonAddress_Country_PlanetId], [t5].[Id] AS [Id0], [t5].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o4] + INNER JOIN ( + SELECT [o5].[Id], [t4].[Id] AS [Id0] + FROM [OwnedPerson] AS [o5] + INNER JOIN ( + SELECT [o6].[Id], [o6].[Discriminator] + FROM [OwnedPerson] AS [o6] + WHERE [o6].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t4] ON [o5].[Id] = [t4].[Id] + ) AS [t5] ON [o4].[Id] = [t5].[Id] + WHERE [o4].[PersonAddress_Country_PlanetId] IS NOT NULL +) AS [t6] ON [t3].[Id] = [t6].[Id] +LEFT JOIN ( + SELECT [o7].[Id], [t7].[Id] AS [Id0] + FROM [OwnedPerson] AS [o7] + INNER JOIN ( + SELECT [o8].[Id], [o8].[Discriminator] + FROM [OwnedPerson] AS [o8] + WHERE [o8].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t7] ON [o7].[Id] = [t7].[Id] +) AS [t8] ON [t].[Id] = [t8].[Id] +LEFT JOIN ( + SELECT [o9].[Id], [o9].[BranchAddress_Country_Name], [o9].[BranchAddress_Country_PlanetId], [t10].[Id] AS [Id0], [t10].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o9] + INNER JOIN ( + SELECT [o10].[Id], [t9].[Id] AS [Id0] + FROM [OwnedPerson] AS [o10] + INNER JOIN ( + SELECT [o11].[Id], [o11].[Discriminator] + FROM [OwnedPerson] AS [o11] + WHERE [o11].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t9] ON [o10].[Id] = [t9].[Id] + ) AS [t10] ON [o9].[Id] = [t10].[Id] + WHERE [o9].[BranchAddress_Country_PlanetId] IS NOT NULL +) AS [t11] ON [t8].[Id] = [t11].[Id] +LEFT JOIN ( + SELECT [o12].[Id], [t12].[Id] AS [Id0] + FROM [OwnedPerson] AS [o12] + INNER JOIN ( + SELECT [o13].[Id], [o13].[Discriminator] + FROM [OwnedPerson] AS [o13] + WHERE [o13].[Discriminator] = N'LeafB' + ) AS [t12] ON [o12].[Id] = [t12].[Id] +) AS [t13] ON [t].[Id] = [t13].[Id] +LEFT JOIN ( + SELECT [o14].[Id], [o14].[LeafBAddress_Country_Name], [o14].[LeafBAddress_Country_PlanetId], [t15].[Id] AS [Id0], [t15].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o14] + INNER JOIN ( + SELECT [o15].[Id], [t14].[Id] AS [Id0] + FROM [OwnedPerson] AS [o15] + INNER JOIN ( + SELECT [o16].[Id], [o16].[Discriminator] + FROM [OwnedPerson] AS [o16] + WHERE [o16].[Discriminator] = N'LeafB' + ) AS [t14] ON [o15].[Id] = [t14].[Id] + ) AS [t15] ON [o14].[Id] = [t15].[Id] + WHERE [o14].[LeafBAddress_Country_PlanetId] IS NOT NULL +) AS [t16] ON [t13].[Id] = [t16].[Id] +LEFT JOIN ( + SELECT [o17].[Id], [t17].[Id] AS [Id0] + FROM [OwnedPerson] AS [o17] + INNER JOIN ( + SELECT [o18].[Id], [o18].[Discriminator] + FROM [OwnedPerson] AS [o18] + WHERE [o18].[Discriminator] = N'LeafA' + ) AS [t17] ON [o17].[Id] = [t17].[Id] +) AS [t18] ON [t].[Id] = [t18].[Id] +LEFT JOIN ( + SELECT [o19].[Id], [o19].[LeafAAddress_Country_Name], [o19].[LeafAAddress_Country_PlanetId], [t20].[Id] AS [Id0], [t20].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o19] + INNER JOIN ( + SELECT [o20].[Id], [t19].[Id] AS [Id0] + FROM [OwnedPerson] AS [o20] + INNER JOIN ( + SELECT [o21].[Id], [o21].[Discriminator] + FROM [OwnedPerson] AS [o21] + WHERE [o21].[Discriminator] = N'LeafA' + ) AS [t19] ON [o20].[Id] = [t19].[Id] + ) AS [t20] ON [o19].[Id] = [t20].[Id] + WHERE [o19].[LeafAAddress_Country_PlanetId] IS NOT NULL +) AS [t21] ON [t18].[Id] = [t21].[Id] +LEFT JOIN [Order] AS [o22] ON [t].[Id] = [o22].[ClientId] +ORDER BY [t].[Id], [o22].[ClientId], [o22].[Id]"); + } + + public override async Task Client_method_take_loads_owned_navigations(bool async) + { + await base.Client_method_take_loads_owned_navigations(async); + + AssertSql( + @"@__p_0='2' + +SELECT [t].[Id], [t].[Discriminator], [t3].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name], [t6].[PersonAddress_Country_PlanetId], [t8].[Id], [t11].[Id], [t11].[BranchAddress_Country_Name], [t11].[BranchAddress_Country_PlanetId], [t13].[Id], [t16].[Id], [t16].[LeafBAddress_Country_Name], [t16].[LeafBAddress_Country_PlanetId], [t18].[Id], [t21].[Id], [t21].[LeafAAddress_Country_Name], [t21].[LeafAAddress_Country_PlanetId], [o22].[ClientId], [o22].[Id] +FROM ( + SELECT TOP(@__p_0) [o].[Id], [o].[Discriminator] + FROM [OwnedPerson] AS [o] + WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ORDER BY [o].[Id] +) AS [t] +LEFT JOIN ( + SELECT [o0].[Id], [t0].[Id] AS [Id0] + FROM [OwnedPerson] AS [o0] + INNER JOIN ( + SELECT [o1].[Id], [o1].[Discriminator] + FROM [OwnedPerson] AS [o1] + WHERE [o1].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t0] ON [o0].[Id] = [t0].[Id] +) AS [t1] ON [t].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [o2].[Id], [t2].[Id] AS [Id0] + FROM [OwnedPerson] AS [o2] + INNER JOIN ( + SELECT [o3].[Id], [o3].[Discriminator] + FROM [OwnedPerson] AS [o3] + WHERE [o3].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t2] ON [o2].[Id] = [t2].[Id] +) AS [t3] ON [t].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [o4].[Id], [o4].[PersonAddress_Country_Name], [o4].[PersonAddress_Country_PlanetId], [t5].[Id] AS [Id0], [t5].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o4] + INNER JOIN ( + SELECT [o5].[Id], [t4].[Id] AS [Id0] + FROM [OwnedPerson] AS [o5] + INNER JOIN ( + SELECT [o6].[Id], [o6].[Discriminator] + FROM [OwnedPerson] AS [o6] + WHERE [o6].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t4] ON [o5].[Id] = [t4].[Id] + ) AS [t5] ON [o4].[Id] = [t5].[Id] + WHERE [o4].[PersonAddress_Country_PlanetId] IS NOT NULL +) AS [t6] ON [t3].[Id] = [t6].[Id] +LEFT JOIN ( + SELECT [o7].[Id], [t7].[Id] AS [Id0] + FROM [OwnedPerson] AS [o7] + INNER JOIN ( + SELECT [o8].[Id], [o8].[Discriminator] + FROM [OwnedPerson] AS [o8] + WHERE [o8].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t7] ON [o7].[Id] = [t7].[Id] +) AS [t8] ON [t].[Id] = [t8].[Id] +LEFT JOIN ( + SELECT [o9].[Id], [o9].[BranchAddress_Country_Name], [o9].[BranchAddress_Country_PlanetId], [t10].[Id] AS [Id0], [t10].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o9] + INNER JOIN ( + SELECT [o10].[Id], [t9].[Id] AS [Id0] + FROM [OwnedPerson] AS [o10] + INNER JOIN ( + SELECT [o11].[Id], [o11].[Discriminator] + FROM [OwnedPerson] AS [o11] + WHERE [o11].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t9] ON [o10].[Id] = [t9].[Id] + ) AS [t10] ON [o9].[Id] = [t10].[Id] + WHERE [o9].[BranchAddress_Country_PlanetId] IS NOT NULL +) AS [t11] ON [t8].[Id] = [t11].[Id] +LEFT JOIN ( + SELECT [o12].[Id], [t12].[Id] AS [Id0] + FROM [OwnedPerson] AS [o12] + INNER JOIN ( + SELECT [o13].[Id], [o13].[Discriminator] + FROM [OwnedPerson] AS [o13] + WHERE [o13].[Discriminator] = N'LeafB' + ) AS [t12] ON [o12].[Id] = [t12].[Id] +) AS [t13] ON [t].[Id] = [t13].[Id] +LEFT JOIN ( + SELECT [o14].[Id], [o14].[LeafBAddress_Country_Name], [o14].[LeafBAddress_Country_PlanetId], [t15].[Id] AS [Id0], [t15].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o14] + INNER JOIN ( + SELECT [o15].[Id], [t14].[Id] AS [Id0] + FROM [OwnedPerson] AS [o15] + INNER JOIN ( + SELECT [o16].[Id], [o16].[Discriminator] + FROM [OwnedPerson] AS [o16] + WHERE [o16].[Discriminator] = N'LeafB' + ) AS [t14] ON [o15].[Id] = [t14].[Id] + ) AS [t15] ON [o14].[Id] = [t15].[Id] + WHERE [o14].[LeafBAddress_Country_PlanetId] IS NOT NULL +) AS [t16] ON [t13].[Id] = [t16].[Id] +LEFT JOIN ( + SELECT [o17].[Id], [t17].[Id] AS [Id0] + FROM [OwnedPerson] AS [o17] + INNER JOIN ( + SELECT [o18].[Id], [o18].[Discriminator] + FROM [OwnedPerson] AS [o18] + WHERE [o18].[Discriminator] = N'LeafA' + ) AS [t17] ON [o17].[Id] = [t17].[Id] +) AS [t18] ON [t].[Id] = [t18].[Id] +LEFT JOIN ( + SELECT [o19].[Id], [o19].[LeafAAddress_Country_Name], [o19].[LeafAAddress_Country_PlanetId], [t20].[Id] AS [Id0], [t20].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o19] + INNER JOIN ( + SELECT [o20].[Id], [t19].[Id] AS [Id0] + FROM [OwnedPerson] AS [o20] + INNER JOIN ( + SELECT [o21].[Id], [o21].[Discriminator] + FROM [OwnedPerson] AS [o21] + WHERE [o21].[Discriminator] = N'LeafA' + ) AS [t19] ON [o20].[Id] = [t19].[Id] + ) AS [t20] ON [o19].[Id] = [t20].[Id] + WHERE [o19].[LeafAAddress_Country_PlanetId] IS NOT NULL +) AS [t21] ON [t18].[Id] = [t21].[Id] +LEFT JOIN [Order] AS [o22] ON [t].[Id] = [o22].[ClientId] +ORDER BY [t].[Id], [o22].[ClientId], [o22].[Id]"); + } + + public override async Task Client_method_skip_take_loads_owned_navigations(bool async) + { + await base.Client_method_skip_take_loads_owned_navigations(async); + + AssertSql( + @"@__p_0='1' +@__p_1='2' + +SELECT [t].[Id], [t].[Discriminator], [t3].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name], [t6].[PersonAddress_Country_PlanetId], [t8].[Id], [t11].[Id], [t11].[BranchAddress_Country_Name], [t11].[BranchAddress_Country_PlanetId], [t13].[Id], [t16].[Id], [t16].[LeafBAddress_Country_Name], [t16].[LeafBAddress_Country_PlanetId], [t18].[Id], [t21].[Id], [t21].[LeafAAddress_Country_Name], [t21].[LeafAAddress_Country_PlanetId], [o22].[ClientId], [o22].[Id] +FROM ( + SELECT [o].[Id], [o].[Discriminator] + FROM [OwnedPerson] AS [o] + WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ORDER BY [o].[Id] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t] +LEFT JOIN ( + SELECT [o0].[Id], [t0].[Id] AS [Id0] + FROM [OwnedPerson] AS [o0] + INNER JOIN ( + SELECT [o1].[Id], [o1].[Discriminator] + FROM [OwnedPerson] AS [o1] + WHERE [o1].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t0] ON [o0].[Id] = [t0].[Id] +) AS [t1] ON [t].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [o2].[Id], [t2].[Id] AS [Id0] + FROM [OwnedPerson] AS [o2] + INNER JOIN ( + SELECT [o3].[Id], [o3].[Discriminator] + FROM [OwnedPerson] AS [o3] + WHERE [o3].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t2] ON [o2].[Id] = [t2].[Id] +) AS [t3] ON [t].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [o4].[Id], [o4].[PersonAddress_Country_Name], [o4].[PersonAddress_Country_PlanetId], [t5].[Id] AS [Id0], [t5].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o4] + INNER JOIN ( + SELECT [o5].[Id], [t4].[Id] AS [Id0] + FROM [OwnedPerson] AS [o5] + INNER JOIN ( + SELECT [o6].[Id], [o6].[Discriminator] + FROM [OwnedPerson] AS [o6] + WHERE [o6].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t4] ON [o5].[Id] = [t4].[Id] + ) AS [t5] ON [o4].[Id] = [t5].[Id] + WHERE [o4].[PersonAddress_Country_PlanetId] IS NOT NULL +) AS [t6] ON [t3].[Id] = [t6].[Id] +LEFT JOIN ( + SELECT [o7].[Id], [t7].[Id] AS [Id0] + FROM [OwnedPerson] AS [o7] + INNER JOIN ( + SELECT [o8].[Id], [o8].[Discriminator] + FROM [OwnedPerson] AS [o8] + WHERE [o8].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t7] ON [o7].[Id] = [t7].[Id] +) AS [t8] ON [t].[Id] = [t8].[Id] +LEFT JOIN ( + SELECT [o9].[Id], [o9].[BranchAddress_Country_Name], [o9].[BranchAddress_Country_PlanetId], [t10].[Id] AS [Id0], [t10].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o9] + INNER JOIN ( + SELECT [o10].[Id], [t9].[Id] AS [Id0] + FROM [OwnedPerson] AS [o10] + INNER JOIN ( + SELECT [o11].[Id], [o11].[Discriminator] + FROM [OwnedPerson] AS [o11] + WHERE [o11].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t9] ON [o10].[Id] = [t9].[Id] + ) AS [t10] ON [o9].[Id] = [t10].[Id] + WHERE [o9].[BranchAddress_Country_PlanetId] IS NOT NULL +) AS [t11] ON [t8].[Id] = [t11].[Id] +LEFT JOIN ( + SELECT [o12].[Id], [t12].[Id] AS [Id0] + FROM [OwnedPerson] AS [o12] + INNER JOIN ( + SELECT [o13].[Id], [o13].[Discriminator] + FROM [OwnedPerson] AS [o13] + WHERE [o13].[Discriminator] = N'LeafB' + ) AS [t12] ON [o12].[Id] = [t12].[Id] +) AS [t13] ON [t].[Id] = [t13].[Id] +LEFT JOIN ( + SELECT [o14].[Id], [o14].[LeafBAddress_Country_Name], [o14].[LeafBAddress_Country_PlanetId], [t15].[Id] AS [Id0], [t15].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o14] + INNER JOIN ( + SELECT [o15].[Id], [t14].[Id] AS [Id0] + FROM [OwnedPerson] AS [o15] + INNER JOIN ( + SELECT [o16].[Id], [o16].[Discriminator] + FROM [OwnedPerson] AS [o16] + WHERE [o16].[Discriminator] = N'LeafB' + ) AS [t14] ON [o15].[Id] = [t14].[Id] + ) AS [t15] ON [o14].[Id] = [t15].[Id] + WHERE [o14].[LeafBAddress_Country_PlanetId] IS NOT NULL +) AS [t16] ON [t13].[Id] = [t16].[Id] +LEFT JOIN ( + SELECT [o17].[Id], [t17].[Id] AS [Id0] + FROM [OwnedPerson] AS [o17] + INNER JOIN ( + SELECT [o18].[Id], [o18].[Discriminator] + FROM [OwnedPerson] AS [o18] + WHERE [o18].[Discriminator] = N'LeafA' + ) AS [t17] ON [o17].[Id] = [t17].[Id] +) AS [t18] ON [t].[Id] = [t18].[Id] +LEFT JOIN ( + SELECT [o19].[Id], [o19].[LeafAAddress_Country_Name], [o19].[LeafAAddress_Country_PlanetId], [t20].[Id] AS [Id0], [t20].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o19] + INNER JOIN ( + SELECT [o20].[Id], [t19].[Id] AS [Id0] + FROM [OwnedPerson] AS [o20] + INNER JOIN ( + SELECT [o21].[Id], [o21].[Discriminator] + FROM [OwnedPerson] AS [o21] + WHERE [o21].[Discriminator] = N'LeafA' + ) AS [t19] ON [o20].[Id] = [t19].[Id] + ) AS [t20] ON [o19].[Id] = [t20].[Id] + WHERE [o19].[LeafAAddress_Country_PlanetId] IS NOT NULL +) AS [t21] ON [t18].[Id] = [t21].[Id] +LEFT JOIN [Order] AS [o22] ON [t].[Id] = [o22].[ClientId] +ORDER BY [t].[Id], [o22].[ClientId], [o22].[Id]"); + } + + public override async Task Client_method_skip_loads_owned_navigations_variation_2(bool async) + { + await base.Client_method_skip_loads_owned_navigations_variation_2(async); + + AssertSql( + @"@__p_0='1' + +SELECT [t].[Id], [t].[Discriminator], [t3].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name], [t6].[PersonAddress_Country_PlanetId], [t8].[Id], [t11].[Id], [t11].[BranchAddress_Country_Name], [t11].[BranchAddress_Country_PlanetId], [t13].[Id], [t16].[Id], [t16].[LeafBAddress_Country_Name], [t16].[LeafBAddress_Country_PlanetId], [t18].[Id], [t21].[Id], [t21].[LeafAAddress_Country_Name], [t21].[LeafAAddress_Country_PlanetId], [o22].[ClientId], [o22].[Id] +FROM ( + SELECT [o].[Id], [o].[Discriminator] + FROM [OwnedPerson] AS [o] + WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ORDER BY [o].[Id] + OFFSET @__p_0 ROWS +) AS [t] +LEFT JOIN ( + SELECT [o0].[Id], [t0].[Id] AS [Id0] + FROM [OwnedPerson] AS [o0] + INNER JOIN ( + SELECT [o1].[Id], [o1].[Discriminator] + FROM [OwnedPerson] AS [o1] + WHERE [o1].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t0] ON [o0].[Id] = [t0].[Id] +) AS [t1] ON [t].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [o2].[Id], [t2].[Id] AS [Id0] + FROM [OwnedPerson] AS [o2] + INNER JOIN ( + SELECT [o3].[Id], [o3].[Discriminator] + FROM [OwnedPerson] AS [o3] + WHERE [o3].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t2] ON [o2].[Id] = [t2].[Id] +) AS [t3] ON [t].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [o4].[Id], [o4].[PersonAddress_Country_Name], [o4].[PersonAddress_Country_PlanetId], [t5].[Id] AS [Id0], [t5].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o4] + INNER JOIN ( + SELECT [o5].[Id], [t4].[Id] AS [Id0] + FROM [OwnedPerson] AS [o5] + INNER JOIN ( + SELECT [o6].[Id], [o6].[Discriminator] + FROM [OwnedPerson] AS [o6] + WHERE [o6].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t4] ON [o5].[Id] = [t4].[Id] + ) AS [t5] ON [o4].[Id] = [t5].[Id] + WHERE [o4].[PersonAddress_Country_PlanetId] IS NOT NULL +) AS [t6] ON [t3].[Id] = [t6].[Id] +LEFT JOIN ( + SELECT [o7].[Id], [t7].[Id] AS [Id0] + FROM [OwnedPerson] AS [o7] + INNER JOIN ( + SELECT [o8].[Id], [o8].[Discriminator] + FROM [OwnedPerson] AS [o8] + WHERE [o8].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t7] ON [o7].[Id] = [t7].[Id] +) AS [t8] ON [t].[Id] = [t8].[Id] +LEFT JOIN ( + SELECT [o9].[Id], [o9].[BranchAddress_Country_Name], [o9].[BranchAddress_Country_PlanetId], [t10].[Id] AS [Id0], [t10].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o9] + INNER JOIN ( + SELECT [o10].[Id], [t9].[Id] AS [Id0] + FROM [OwnedPerson] AS [o10] + INNER JOIN ( + SELECT [o11].[Id], [o11].[Discriminator] + FROM [OwnedPerson] AS [o11] + WHERE [o11].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t9] ON [o10].[Id] = [t9].[Id] + ) AS [t10] ON [o9].[Id] = [t10].[Id] + WHERE [o9].[BranchAddress_Country_PlanetId] IS NOT NULL +) AS [t11] ON [t8].[Id] = [t11].[Id] +LEFT JOIN ( + SELECT [o12].[Id], [t12].[Id] AS [Id0] + FROM [OwnedPerson] AS [o12] + INNER JOIN ( + SELECT [o13].[Id], [o13].[Discriminator] + FROM [OwnedPerson] AS [o13] + WHERE [o13].[Discriminator] = N'LeafB' + ) AS [t12] ON [o12].[Id] = [t12].[Id] +) AS [t13] ON [t].[Id] = [t13].[Id] +LEFT JOIN ( + SELECT [o14].[Id], [o14].[LeafBAddress_Country_Name], [o14].[LeafBAddress_Country_PlanetId], [t15].[Id] AS [Id0], [t15].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o14] + INNER JOIN ( + SELECT [o15].[Id], [t14].[Id] AS [Id0] + FROM [OwnedPerson] AS [o15] + INNER JOIN ( + SELECT [o16].[Id], [o16].[Discriminator] + FROM [OwnedPerson] AS [o16] + WHERE [o16].[Discriminator] = N'LeafB' + ) AS [t14] ON [o15].[Id] = [t14].[Id] + ) AS [t15] ON [o14].[Id] = [t15].[Id] + WHERE [o14].[LeafBAddress_Country_PlanetId] IS NOT NULL +) AS [t16] ON [t13].[Id] = [t16].[Id] +LEFT JOIN ( + SELECT [o17].[Id], [t17].[Id] AS [Id0] + FROM [OwnedPerson] AS [o17] + INNER JOIN ( + SELECT [o18].[Id], [o18].[Discriminator] + FROM [OwnedPerson] AS [o18] + WHERE [o18].[Discriminator] = N'LeafA' + ) AS [t17] ON [o17].[Id] = [t17].[Id] +) AS [t18] ON [t].[Id] = [t18].[Id] +LEFT JOIN ( + SELECT [o19].[Id], [o19].[LeafAAddress_Country_Name], [o19].[LeafAAddress_Country_PlanetId], [t20].[Id] AS [Id0], [t20].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o19] + INNER JOIN ( + SELECT [o20].[Id], [t19].[Id] AS [Id0] + FROM [OwnedPerson] AS [o20] + INNER JOIN ( + SELECT [o21].[Id], [o21].[Discriminator] + FROM [OwnedPerson] AS [o21] + WHERE [o21].[Discriminator] = N'LeafA' + ) AS [t19] ON [o20].[Id] = [t19].[Id] + ) AS [t20] ON [o19].[Id] = [t20].[Id] + WHERE [o19].[LeafAAddress_Country_PlanetId] IS NOT NULL +) AS [t21] ON [t18].[Id] = [t21].[Id] +LEFT JOIN [Order] AS [o22] ON [t].[Id] = [o22].[ClientId] +ORDER BY [t].[Id], [o22].[ClientId], [o22].[Id]"); + } + + public override async Task Client_method_take_loads_owned_navigations_variation_2(bool async) + { + await base.Client_method_take_loads_owned_navigations_variation_2(async); + + AssertSql( + @"@__p_0='2' + +SELECT [t].[Id], [t].[Discriminator], [t3].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name], [t6].[PersonAddress_Country_PlanetId], [t8].[Id], [t11].[Id], [t11].[BranchAddress_Country_Name], [t11].[BranchAddress_Country_PlanetId], [t13].[Id], [t16].[Id], [t16].[LeafBAddress_Country_Name], [t16].[LeafBAddress_Country_PlanetId], [t18].[Id], [t21].[Id], [t21].[LeafAAddress_Country_Name], [t21].[LeafAAddress_Country_PlanetId], [o22].[ClientId], [o22].[Id] +FROM ( + SELECT TOP(@__p_0) [o].[Id], [o].[Discriminator] + FROM [OwnedPerson] AS [o] + WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ORDER BY [o].[Id] +) AS [t] +LEFT JOIN ( + SELECT [o0].[Id], [t0].[Id] AS [Id0] + FROM [OwnedPerson] AS [o0] + INNER JOIN ( + SELECT [o1].[Id], [o1].[Discriminator] + FROM [OwnedPerson] AS [o1] + WHERE [o1].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t0] ON [o0].[Id] = [t0].[Id] +) AS [t1] ON [t].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [o2].[Id], [t2].[Id] AS [Id0] + FROM [OwnedPerson] AS [o2] + INNER JOIN ( + SELECT [o3].[Id], [o3].[Discriminator] + FROM [OwnedPerson] AS [o3] + WHERE [o3].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t2] ON [o2].[Id] = [t2].[Id] +) AS [t3] ON [t].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [o4].[Id], [o4].[PersonAddress_Country_Name], [o4].[PersonAddress_Country_PlanetId], [t5].[Id] AS [Id0], [t5].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o4] + INNER JOIN ( + SELECT [o5].[Id], [t4].[Id] AS [Id0] + FROM [OwnedPerson] AS [o5] + INNER JOIN ( + SELECT [o6].[Id], [o6].[Discriminator] + FROM [OwnedPerson] AS [o6] + WHERE [o6].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t4] ON [o5].[Id] = [t4].[Id] + ) AS [t5] ON [o4].[Id] = [t5].[Id] + WHERE [o4].[PersonAddress_Country_PlanetId] IS NOT NULL +) AS [t6] ON [t3].[Id] = [t6].[Id] +LEFT JOIN ( + SELECT [o7].[Id], [t7].[Id] AS [Id0] + FROM [OwnedPerson] AS [o7] + INNER JOIN ( + SELECT [o8].[Id], [o8].[Discriminator] + FROM [OwnedPerson] AS [o8] + WHERE [o8].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t7] ON [o7].[Id] = [t7].[Id] +) AS [t8] ON [t].[Id] = [t8].[Id] +LEFT JOIN ( + SELECT [o9].[Id], [o9].[BranchAddress_Country_Name], [o9].[BranchAddress_Country_PlanetId], [t10].[Id] AS [Id0], [t10].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o9] + INNER JOIN ( + SELECT [o10].[Id], [t9].[Id] AS [Id0] + FROM [OwnedPerson] AS [o10] + INNER JOIN ( + SELECT [o11].[Id], [o11].[Discriminator] + FROM [OwnedPerson] AS [o11] + WHERE [o11].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t9] ON [o10].[Id] = [t9].[Id] + ) AS [t10] ON [o9].[Id] = [t10].[Id] + WHERE [o9].[BranchAddress_Country_PlanetId] IS NOT NULL +) AS [t11] ON [t8].[Id] = [t11].[Id] +LEFT JOIN ( + SELECT [o12].[Id], [t12].[Id] AS [Id0] + FROM [OwnedPerson] AS [o12] + INNER JOIN ( + SELECT [o13].[Id], [o13].[Discriminator] + FROM [OwnedPerson] AS [o13] + WHERE [o13].[Discriminator] = N'LeafB' + ) AS [t12] ON [o12].[Id] = [t12].[Id] +) AS [t13] ON [t].[Id] = [t13].[Id] +LEFT JOIN ( + SELECT [o14].[Id], [o14].[LeafBAddress_Country_Name], [o14].[LeafBAddress_Country_PlanetId], [t15].[Id] AS [Id0], [t15].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o14] + INNER JOIN ( + SELECT [o15].[Id], [t14].[Id] AS [Id0] + FROM [OwnedPerson] AS [o15] + INNER JOIN ( + SELECT [o16].[Id], [o16].[Discriminator] + FROM [OwnedPerson] AS [o16] + WHERE [o16].[Discriminator] = N'LeafB' + ) AS [t14] ON [o15].[Id] = [t14].[Id] + ) AS [t15] ON [o14].[Id] = [t15].[Id] + WHERE [o14].[LeafBAddress_Country_PlanetId] IS NOT NULL +) AS [t16] ON [t13].[Id] = [t16].[Id] +LEFT JOIN ( + SELECT [o17].[Id], [t17].[Id] AS [Id0] + FROM [OwnedPerson] AS [o17] + INNER JOIN ( + SELECT [o18].[Id], [o18].[Discriminator] + FROM [OwnedPerson] AS [o18] + WHERE [o18].[Discriminator] = N'LeafA' + ) AS [t17] ON [o17].[Id] = [t17].[Id] +) AS [t18] ON [t].[Id] = [t18].[Id] +LEFT JOIN ( + SELECT [o19].[Id], [o19].[LeafAAddress_Country_Name], [o19].[LeafAAddress_Country_PlanetId], [t20].[Id] AS [Id0], [t20].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o19] + INNER JOIN ( + SELECT [o20].[Id], [t19].[Id] AS [Id0] + FROM [OwnedPerson] AS [o20] + INNER JOIN ( + SELECT [o21].[Id], [o21].[Discriminator] + FROM [OwnedPerson] AS [o21] + WHERE [o21].[Discriminator] = N'LeafA' + ) AS [t19] ON [o20].[Id] = [t19].[Id] + ) AS [t20] ON [o19].[Id] = [t20].[Id] + WHERE [o19].[LeafAAddress_Country_PlanetId] IS NOT NULL +) AS [t21] ON [t18].[Id] = [t21].[Id] +LEFT JOIN [Order] AS [o22] ON [t].[Id] = [o22].[ClientId] +ORDER BY [t].[Id], [o22].[ClientId], [o22].[Id]"); + } + + public override async Task Client_method_skip_take_loads_owned_navigations_variation_2(bool async) + { + await base.Client_method_skip_take_loads_owned_navigations_variation_2(async); + + AssertSql( + @"@__p_0='1' +@__p_1='2' + +SELECT [t].[Id], [t].[Discriminator], [t3].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name], [t6].[PersonAddress_Country_PlanetId], [t8].[Id], [t11].[Id], [t11].[BranchAddress_Country_Name], [t11].[BranchAddress_Country_PlanetId], [t13].[Id], [t16].[Id], [t16].[LeafBAddress_Country_Name], [t16].[LeafBAddress_Country_PlanetId], [t18].[Id], [t21].[Id], [t21].[LeafAAddress_Country_Name], [t21].[LeafAAddress_Country_PlanetId], [o22].[ClientId], [o22].[Id] +FROM ( + SELECT [o].[Id], [o].[Discriminator] + FROM [OwnedPerson] AS [o] + WHERE [o].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ORDER BY [o].[Id] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t] +LEFT JOIN ( + SELECT [o0].[Id], [t0].[Id] AS [Id0] + FROM [OwnedPerson] AS [o0] + INNER JOIN ( + SELECT [o1].[Id], [o1].[Discriminator] + FROM [OwnedPerson] AS [o1] + WHERE [o1].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t0] ON [o0].[Id] = [t0].[Id] +) AS [t1] ON [t].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [o2].[Id], [t2].[Id] AS [Id0] + FROM [OwnedPerson] AS [o2] + INNER JOIN ( + SELECT [o3].[Id], [o3].[Discriminator] + FROM [OwnedPerson] AS [o3] + WHERE [o3].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t2] ON [o2].[Id] = [t2].[Id] +) AS [t3] ON [t].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [o4].[Id], [o4].[PersonAddress_Country_Name], [o4].[PersonAddress_Country_PlanetId], [t5].[Id] AS [Id0], [t5].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o4] + INNER JOIN ( + SELECT [o5].[Id], [t4].[Id] AS [Id0] + FROM [OwnedPerson] AS [o5] + INNER JOIN ( + SELECT [o6].[Id], [o6].[Discriminator] + FROM [OwnedPerson] AS [o6] + WHERE [o6].[Discriminator] IN (N'OwnedPerson', N'Branch', N'LeafB', N'LeafA') + ) AS [t4] ON [o5].[Id] = [t4].[Id] + ) AS [t5] ON [o4].[Id] = [t5].[Id] + WHERE [o4].[PersonAddress_Country_PlanetId] IS NOT NULL +) AS [t6] ON [t3].[Id] = [t6].[Id] +LEFT JOIN ( + SELECT [o7].[Id], [t7].[Id] AS [Id0] + FROM [OwnedPerson] AS [o7] + INNER JOIN ( + SELECT [o8].[Id], [o8].[Discriminator] + FROM [OwnedPerson] AS [o8] + WHERE [o8].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t7] ON [o7].[Id] = [t7].[Id] +) AS [t8] ON [t].[Id] = [t8].[Id] +LEFT JOIN ( + SELECT [o9].[Id], [o9].[BranchAddress_Country_Name], [o9].[BranchAddress_Country_PlanetId], [t10].[Id] AS [Id0], [t10].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o9] + INNER JOIN ( + SELECT [o10].[Id], [t9].[Id] AS [Id0] + FROM [OwnedPerson] AS [o10] + INNER JOIN ( + SELECT [o11].[Id], [o11].[Discriminator] + FROM [OwnedPerson] AS [o11] + WHERE [o11].[Discriminator] IN (N'Branch', N'LeafA') + ) AS [t9] ON [o10].[Id] = [t9].[Id] + ) AS [t10] ON [o9].[Id] = [t10].[Id] + WHERE [o9].[BranchAddress_Country_PlanetId] IS NOT NULL +) AS [t11] ON [t8].[Id] = [t11].[Id] +LEFT JOIN ( + SELECT [o12].[Id], [t12].[Id] AS [Id0] + FROM [OwnedPerson] AS [o12] + INNER JOIN ( + SELECT [o13].[Id], [o13].[Discriminator] + FROM [OwnedPerson] AS [o13] + WHERE [o13].[Discriminator] = N'LeafB' + ) AS [t12] ON [o12].[Id] = [t12].[Id] +) AS [t13] ON [t].[Id] = [t13].[Id] +LEFT JOIN ( + SELECT [o14].[Id], [o14].[LeafBAddress_Country_Name], [o14].[LeafBAddress_Country_PlanetId], [t15].[Id] AS [Id0], [t15].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o14] + INNER JOIN ( + SELECT [o15].[Id], [t14].[Id] AS [Id0] + FROM [OwnedPerson] AS [o15] + INNER JOIN ( + SELECT [o16].[Id], [o16].[Discriminator] + FROM [OwnedPerson] AS [o16] + WHERE [o16].[Discriminator] = N'LeafB' + ) AS [t14] ON [o15].[Id] = [t14].[Id] + ) AS [t15] ON [o14].[Id] = [t15].[Id] + WHERE [o14].[LeafBAddress_Country_PlanetId] IS NOT NULL +) AS [t16] ON [t13].[Id] = [t16].[Id] +LEFT JOIN ( + SELECT [o17].[Id], [t17].[Id] AS [Id0] + FROM [OwnedPerson] AS [o17] + INNER JOIN ( + SELECT [o18].[Id], [o18].[Discriminator] + FROM [OwnedPerson] AS [o18] + WHERE [o18].[Discriminator] = N'LeafA' + ) AS [t17] ON [o17].[Id] = [t17].[Id] +) AS [t18] ON [t].[Id] = [t18].[Id] +LEFT JOIN ( + SELECT [o19].[Id], [o19].[LeafAAddress_Country_Name], [o19].[LeafAAddress_Country_PlanetId], [t20].[Id] AS [Id0], [t20].[Id0] AS [Id00] + FROM [OwnedPerson] AS [o19] + INNER JOIN ( + SELECT [o20].[Id], [t19].[Id] AS [Id0] + FROM [OwnedPerson] AS [o20] + INNER JOIN ( + SELECT [o21].[Id], [o21].[Discriminator] + FROM [OwnedPerson] AS [o21] + WHERE [o21].[Discriminator] = N'LeafA' + ) AS [t19] ON [o20].[Id] = [t19].[Id] + ) AS [t20] ON [o19].[Id] = [t20].[Id] + WHERE [o19].[LeafAAddress_Country_PlanetId] IS NOT NULL +) AS [t21] ON [t18].[Id] = [t21].[Id] +LEFT JOIN [Order] AS [o22] ON [t].[Id] = [o22].[ClientId] +ORDER BY [t].[Id], [o22].[ClientId], [o22].[Id]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs index 2947b06cf33..145dd9db521 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs @@ -1269,7 +1269,7 @@ join eRoot in ctx.Entities.Include(e => e.Children) on eVersion.RootEntityId equals eRoot.Id into RootEntities from eRootJoined in RootEntities.DefaultIfEmpty() - // ReSharper disable once ConstantNullCoalescingCondition + // ReSharper disable once ConstantNullCoalescingCondition select new { One = 1, Coalesce = eRootJoined ?? (eVersion ?? eRootJoined) }; var result = query.ToList(); @@ -1288,7 +1288,7 @@ join eRoot in ctx.Entities on eVersion.RootEntityId equals eRoot.Id into RootEntities from eRootJoined in RootEntities.DefaultIfEmpty() - // ReSharper disable once ConstantNullCoalescingCondition + // ReSharper disable once ConstantNullCoalescingCondition select new { One = eRootJoined, @@ -1312,7 +1312,7 @@ join eRoot in ctx.Entities on eVersion.RootEntityId equals eRoot.Id into RootEntities from eRootJoined in RootEntities.DefaultIfEmpty() - // ReSharper disable once MergeConditionalExpression + // ReSharper disable once MergeConditionalExpression #pragma warning disable IDE0029 // Use coalesce expression select eRootJoined != null ? eRootJoined : eVersion; #pragma warning restore IDE0029 // Use coalesce expression @@ -6862,6 +6862,89 @@ public BugContext18759(DbContextOptions options) #endregion + #region Issue19138 + + [ConditionalFact] + public void Accessing_scalar_property_in_derived_type_projection_does_not_load_owned_navigations() + { + using var _ = CreateDatabase19138(); + using var context = new BugContext19138(_options); + + var result = context.BaseEntities + .Select(b => context.OtherEntities.Where(o => o.OtherEntityData == ((SubEntity19138)b).Data).FirstOrDefault()) + .ToList(); + + Assert.Equal("A", Assert.Single(result).OtherEntityData); + + AssertSql( + @"SELECT [t0].[Id], [t0].[OtherEntityData] +FROM [BaseEntities] AS [b] +LEFT JOIN ( + SELECT [t].[Id], [t].[OtherEntityData] + FROM ( + SELECT [o].[Id], [o].[OtherEntityData], ROW_NUMBER() OVER(PARTITION BY [o].[OtherEntityData] ORDER BY [o].[Id]) AS [row] + FROM [OtherEntities] AS [o] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [b].[Data] = [t0].[OtherEntityData] +WHERE [b].[Discriminator] IN (N'BaseEntity19138', N'SubEntity19138')"); + } + + private SqlServerTestStore CreateDatabase19138() + => CreateTestStore( + () => new BugContext19138(_options), + context => + { + context.Add(new OtherEntity19138 { OtherEntityData = "A" }); + context.Add(new SubEntity19138 { Data = "A" }); + + context.SaveChanges(); + + ClearLog(); + }); + + private class BaseEntity19138 + { + public int Id { get; set; } + } + + private class SubEntity19138 : BaseEntity19138 + { + public string Data { get; set; } + public Owned19138 Owned { get; set; } + } + + private class Owned19138 + { + public string OwnedData { get; set; } + } + + private class OtherEntity19138 + { + public int Id { get; set; } + public string OtherEntityData { get; set; } + } + + private class BugContext19138 : DbContext + { + public DbSet BaseEntities { get; set; } + public DbSet OtherEntities { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(); + modelBuilder.Entity().OwnsOne(se => se.Owned); + modelBuilder.Entity(); + } + + public BugContext19138(DbContextOptions options) + : base(options) + { + } + } + + #endregion + private DbContextOptions _options; private SqlServerTestStore CreateTestStore(