From 4cda7b26c24a36aaf830377ed6597c4200468c55 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Mon, 21 Oct 2019 15:18:20 -0700 Subject: [PATCH] Query: Expand navigations in Selector/Include in one pass Issue: Earlier we expanded navigation in selector and include in separate phase. This causes issue because if the latter visitor expands a reference navigation then former visitor's collection expansions have incorrect references. Fix: Fix is to make include expansion part of selector expansion as next phase. So by the time we apply include, the correlation predicate in collection hasn't been converted to actual reference. So when it gets converted, it takes correct reference. Also apply pending selector inside lambda expression since it is a subquery. This caused issue when subquery has a projection which has navigation to expand, which we never visited. Resolves #18127 Resolves #18090 Resolves #17852 Resolves #17756 --- ...ingExpressionVisitor.ExpressionVisitors.cs | 59 ++-- .../NavigationExpandingExpressionVisitor.cs | 37 +-- .../Query/ComplexNavigationsQueryTestBase.cs | 313 ++++++++++-------- .../ComplexNavigationsWeakQueryTestBase.cs | 4 +- .../Query/GearsOfWarQueryTestBase.cs | 12 +- .../Query/SimpleQueryTestBase.cs | 2 +- .../ComplexNavigationsQuerySqlServerTest.cs | 56 +++- .../Query/GearsOfWarQuerySqlServerTest.cs | 26 ++ .../Query/SimpleQuerySqlServerTest.cs | 9 +- 9 files changed, 300 insertions(+), 218 deletions(-) diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index c478b80504d..a5d64b793bc 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -27,6 +27,18 @@ private class ExpandingExpressionVisitor : ExpressionVisitor _source = source; } + public Expression Expand(Expression expression, bool applyIncludes = false) + { + expression = Visit(expression); + if (applyIncludes) + { + expression = new IncludeExpandingExpressionVisitor(_navigationExpandingExpressionVisitor, _source) + .Visit(expression); + } + + return expression; + } + protected override Expression VisitExtension(Expression expression) { switch (expression) @@ -303,11 +315,10 @@ private class IncludeExpandingExpressionVisitor : ExpandingExpressionVisitor public IncludeExpandingExpressionVisitor( NavigationExpandingExpressionVisitor navigationExpandingExpressionVisitor, - NavigationExpansionExpression source, - bool tracking) + NavigationExpansionExpression source) : base(navigationExpandingExpressionVisitor, source) { - _isTracking = tracking; + _isTracking = navigationExpandingExpressionVisitor._queryCompilationContext.IsTracking; } public override Expression Visit(Expression expression) @@ -501,42 +512,16 @@ private Expression ExpandIncludesHelper(Expression root, EntityReference entityR } } - private class IncludeApplyingExpressionVisitor : ExpressionVisitor - { - private readonly NavigationExpandingExpressionVisitor _visitor; - private readonly bool _isTracking; - - public IncludeApplyingExpressionVisitor(NavigationExpandingExpressionVisitor visitor, bool tracking) - { - _visitor = visitor; - _isTracking = tracking; - } - - public override Expression Visit(Expression expression) - { - if (expression is NavigationExpansionExpression navigationExpansionExpression) - { - var innerVisitor = new IncludeExpandingExpressionVisitor(_visitor, navigationExpansionExpression, _isTracking); - var pendingSelector = innerVisitor.Visit(navigationExpansionExpression.PendingSelector); - pendingSelector = _visitor.Visit(pendingSelector); - pendingSelector = Visit(pendingSelector); - - navigationExpansionExpression.ApplySelector(pendingSelector); - - return navigationExpansionExpression; - } - - return base.Visit(expression); - } - } - private class PendingSelectorExpandingExpressionVisitor : ExpressionVisitor { private readonly NavigationExpandingExpressionVisitor _visitor; + private readonly bool _applyIncludes; - public PendingSelectorExpandingExpressionVisitor(NavigationExpandingExpressionVisitor visitor) + public PendingSelectorExpandingExpressionVisitor( + NavigationExpandingExpressionVisitor visitor, bool applyIncludes = false) { _visitor = visitor; + _applyIncludes = applyIncludes; } public override Expression Visit(Expression expression) @@ -545,11 +530,11 @@ public override Expression Visit(Expression expression) { _visitor.ApplyPendingOrderings(navigationExpansionExpression); - var pendingSelector = _visitor.ExpandNavigationsInExpression( - navigationExpansionExpression, navigationExpansionExpression.PendingSelector); - + var pendingSelector = new ExpandingExpressionVisitor(_visitor, navigationExpansionExpression) + .Expand(navigationExpansionExpression.PendingSelector, _applyIncludes); + pendingSelector = _visitor._subqueryMemberPushdownExpressionVisitor.Visit(pendingSelector); + pendingSelector = _visitor.Visit(pendingSelector); pendingSelector = Visit(pendingSelector); - navigationExpansionExpression.ApplySelector(pendingSelector); return navigationExpansionExpression; diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index c27b767b357..ed93fb9996e 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -67,7 +67,7 @@ private string GetParameterName(string prefix) public virtual Expression Expand(Expression query) { - var result = ExpandAndReduce(query, applyInclude: true); + var result = ExpandAndReduce(query, applyIncludes: true); var dbContextOnQueryContextPropertyAccess = Expression.Convert( @@ -96,15 +96,10 @@ public virtual Expression Expand(Expression query) return result; } - private Expression ExpandAndReduce(Expression query, bool applyInclude) + private Expression ExpandAndReduce(Expression query, bool applyIncludes) { var result = Visit(query); - result = _pendingSelectorExpandingExpressionVisitor.Visit(result); - if (applyInclude) - { - result = new IncludeApplyingExpressionVisitor(this, _queryCompilationContext.IsTracking).Visit(result); - } - + result = new PendingSelectorExpandingExpressionVisitor(this, applyIncludes: applyIncludes).Visit(result); result = Reduce(result); return result; @@ -131,7 +126,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio navigationExpansionExpression = (NavigationExpansionExpression)Visit(processedDefiningQueryBody); - var expanded = ExpandAndReduce(navigationExpansionExpression, applyInclude: false); + var expanded = ExpandAndReduce(navigationExpansionExpression, applyIncludes: false); navigationExpansionExpression = CreateNavigationExpansionExpression(expanded, entityType); } else @@ -178,7 +173,7 @@ private Expression ApplyQueryFilter(NavigationExpansionExpression navigationExpa QueryableMethods.Where.MakeGenericMethod(rootEntityType.ClrType), NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(rootEntityType.ClrType), filterPredicate); - var rewrittenFilterWrapper = (MethodCallExpression)_entityEqualityRewritingExpressionVisitor.Rewrite(filterWrapper); + var rewrittenFilterWrapper = (MethodCallExpression)_entityEqualityRewritingExpressionVisitor.Rewrite(filterWrapper); filterPredicate = rewrittenFilterWrapper.Arguments[1].UnwrapLambdaFromQuote(); _parameterizedQueryFilterPredicateCache[rootEntityType] = filterPredicate; @@ -950,9 +945,8 @@ private bool CompareIncludes(Expression outer, Expression inner) else { source = (NavigationExpansionExpression)ProcessSelect(source, elementSelector); - source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); - source = (NavigationExpansionExpression)new IncludeApplyingExpressionVisitor( - this, _queryCompilationContext.IsTracking).Visit(source); + source = (NavigationExpansionExpression)new PendingSelectorExpandingExpressionVisitor(this, applyIncludes: true) + .Visit(source); keySelector = GenerateLambda(keySelectorBody, source.CurrentParameter); elementSelector = GenerateLambda(source.PendingSelector, source.CurrentParameter); if (resultSelector == null) @@ -1336,7 +1330,6 @@ private Expression SnapshotExpression(Expression selector) && selector.ReturnType.GetGenericTypeDefinition() == typeof(IGrouping<,>))) { var selectorLambda = GenerateLambda(ExpandNavigationsInLambdaExpression(source, selector), source.CurrentParameter); - var newSource = Expression.Call( QueryableMethods.Select.MakeGenericMethod(source.SourceElementType, selectorLambda.ReturnType), source.Source, @@ -1420,15 +1413,6 @@ private Expression Reduce(Expression source) return _reducingExpressionVisitor.Visit(source); } - private Expression ExpandNavigationsInExpression(NavigationExpansionExpression source, Expression expression) - { - expression = new ExpandingExpressionVisitor(this, source).Visit(expression); - expression = _subqueryMemberPushdownExpressionVisitor.Visit(expression); - expression = Visit(expression); - - return expression; - } - private Expression ExpandNavigationsInLambdaExpression( NavigationExpansionExpression source, LambdaExpression lambdaExpression) @@ -1438,7 +1422,12 @@ private Expression ExpandNavigationsInExpression(NavigationExpansionExpression s source.PendingSelector, lambdaExpression.Body); - return ExpandNavigationsInExpression(source, lambdaBody); + lambdaBody = new ExpandingExpressionVisitor(this, source).Visit(lambdaBody); + lambdaBody = _subqueryMemberPushdownExpressionVisitor.Visit(lambdaBody); + lambdaBody = Visit(lambdaBody); + lambdaBody = _pendingSelectorExpandingExpressionVisitor.Visit(lambdaBody); + + return lambdaBody; } private class Parameters : IParameterValues diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs index 06610af5479..dd984e2f70f 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs @@ -2172,8 +2172,8 @@ public virtual Task Order_by_key_of_navigation_similar_to_projected_gets_optimiz return AssertQuery( isAsync, ss => from l3 in ss.Set() - orderby l3.OneToOne_Required_FK_Inverse3.Id - select l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_FK_Inverse2, + orderby l3.OneToOne_Required_FK_Inverse3.Id + select l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_FK_Inverse2, elementAsserter: (e, a) => AssertEqual(e, a), assertOrder: true); } @@ -2959,16 +2959,16 @@ public virtual Task Multiple_SelectMany_with_nested_navigations_and_explicit_Def () => Maybe( l1.OneToOne_Required_FK1.OneToOne_Optional_FK2, () => l1.OneToOne_Required_FK1.OneToOne_Optional_FK2.OneToMany_Required3)))) - join l2 in ss.Set().SelectMany( - l4 => MaybeDefaultIfEmpty( - Maybe( - l4.OneToOne_Required_FK_Inverse4, - () => Maybe( - l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3, - () => l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3 - .OneToMany_Required_Self2)))) - on MaybeScalar(l4, () => l4.Id) equals MaybeScalar(l2, () => l2.Id) - select new { l4, l2 }, + join l2 in ss.Set().SelectMany( + l4 => MaybeDefaultIfEmpty( + Maybe( + l4.OneToOne_Required_FK_Inverse4, + () => Maybe( + l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3, + () => l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3 + .OneToMany_Required_Self2)))) + on MaybeScalar(l4, () => l4.Id) equals MaybeScalar(l2, () => l2.Id) + select new { l4, l2 }, elementSorter: e => (e.l4?.Id, e.l2?.Id), elementAsserter: (e, a) => { @@ -2985,7 +2985,7 @@ public virtual Task SelectMany_with_nested_navigations_and_explicit_DefaultIfEmp isAsync, ss => from l3 in ss.Set().SelectMany( l4 => l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3.OneToMany_Required2.DefaultIfEmpty()) - select l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_PK_Inverse2, + select l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_PK_Inverse2, ss => from l3 in ss.Set().SelectMany( l4 => MaybeDefaultIfEmpty( Maybe( @@ -2993,9 +2993,9 @@ public virtual Task SelectMany_with_nested_navigations_and_explicit_DefaultIfEmp () => Maybe( l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3, () => l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3.OneToMany_Required2)))) - select Maybe( - l3, - () => l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_PK_Inverse2)); + select Maybe( + l3, + () => l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_PK_Inverse2)); } [ConditionalTheory] @@ -3005,15 +3005,15 @@ public virtual Task SelectMany_with_nested_navigations_and_explicit_DefaultIfEmp return AssertQuery( isAsync, ss => from l3 in ss.Set().SelectMany(l1 => l1.OneToOne_Optional_FK1.OneToMany_Optional2.DefaultIfEmpty()) - select l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_PK_Inverse2, + select l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_PK_Inverse2, ss => from l3 in ss.Set().SelectMany( l1 => MaybeDefaultIfEmpty( Maybe( l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2))) - select Maybe( - l3, - () => l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_PK_Inverse2)); + select Maybe( + l3, + () => l3.OneToOne_Required_FK_Inverse3.OneToOne_Required_PK_Inverse2)); } [ConditionalTheory] @@ -3026,18 +3026,18 @@ public virtual Task Complex_SelectMany_with_nested_navigations_and_explicit_Defa join l2 in ss.Set().SelectMany( l4 => l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3.OneToMany_Required_Self2.DefaultIfEmpty()) on l4.Id equals l2.Id - join l3 in ss.Set().SelectMany( - l4 => l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3.OneToMany_Required2.DefaultIfEmpty()) - on l2.Id equals l3.Id into grouping - from l3 in grouping.DefaultIfEmpty() - where l4.OneToMany_Optional_Inverse4.Name != "Foo" - orderby l2.OneToOne_Optional_FK2.Id - select new - { - Entity = l4, - Collection = l2.OneToMany_Optional_Self2.Where(e => e.Id != 42).ToList(), - Property = l3.OneToOne_Optional_FK_Inverse3.OneToOne_Required_FK2.Name - }, + join l3 in ss.Set().SelectMany( + l4 => l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3.OneToMany_Required2.DefaultIfEmpty()) + on l2.Id equals l3.Id into grouping + from l3 in grouping.DefaultIfEmpty() + where l4.OneToMany_Optional_Inverse4.Name != "Foo" + orderby l2.OneToOne_Optional_FK2.Id + select new + { + Entity = l4, + Collection = l2.OneToMany_Optional_Self2.Where(e => e.Id != 42).ToList(), + Property = l3.OneToOne_Optional_FK_Inverse3.OneToOne_Required_FK2.Name + }, ss => from l4 in ss.Set().SelectMany( l1 => MaybeDefaultIfEmpty( Maybe( @@ -3045,49 +3045,49 @@ select new () => Maybe( l1.OneToOne_Required_FK1.OneToOne_Optional_FK2, () => l1.OneToOne_Required_FK1.OneToOne_Optional_FK2.OneToMany_Required3)))) - join l2 in ss.Set().SelectMany( - l4 => MaybeDefaultIfEmpty( - Maybe( - l4.OneToOne_Required_FK_Inverse4, - () => Maybe( - l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3, - () => l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3.OneToMany_Required_Self2)))) - on MaybeScalar(l4, () => l4.Id) equals MaybeScalar(l2, () => l2.Id) - join l3 in ss.Set().SelectMany( - l4 => MaybeDefaultIfEmpty( - Maybe( - l4.OneToOne_Required_FK_Inverse4, - () => Maybe( - l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3, - () => l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3.OneToMany_Required2)))) - on MaybeScalar(l2, () => l2.Id) equals MaybeScalar(l3, () => l3.Id) into grouping - from l3 in grouping.DefaultIfEmpty() - where Maybe( - l4, - () => Maybe( - l4.OneToMany_Optional_Inverse4, - () => l4.OneToMany_Optional_Inverse4.Name)) != "Foo" - orderby MaybeScalar( - l2, - () => MaybeScalar( - l2.OneToOne_Optional_FK2, - () => l2.OneToOne_Optional_FK2.Id)) - select new - { - Entity = l4, - Collection = Maybe( - l2, - () => Maybe( - l2.OneToMany_Optional_Self2, - () => l2.OneToMany_Optional_Self2.Where(e => e.Id != 42).ToList())), - Property = Maybe( - l3, - () => Maybe( - l3.OneToOne_Optional_FK_Inverse3, - () => Maybe( - l3.OneToOne_Optional_FK_Inverse3.OneToOne_Required_FK2, - () => l3.OneToOne_Optional_FK_Inverse3.OneToOne_Required_FK2.Name))) - }, + join l2 in ss.Set().SelectMany( + l4 => MaybeDefaultIfEmpty( + Maybe( + l4.OneToOne_Required_FK_Inverse4, + () => Maybe( + l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3, + () => l4.OneToOne_Required_FK_Inverse4.OneToOne_Optional_FK_Inverse3.OneToMany_Required_Self2)))) + on MaybeScalar(l4, () => l4.Id) equals MaybeScalar(l2, () => l2.Id) + join l3 in ss.Set().SelectMany( + l4 => MaybeDefaultIfEmpty( + Maybe( + l4.OneToOne_Required_FK_Inverse4, + () => Maybe( + l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3, + () => l4.OneToOne_Required_FK_Inverse4.OneToOne_Required_FK_Inverse3.OneToMany_Required2)))) + on MaybeScalar(l2, () => l2.Id) equals MaybeScalar(l3, () => l3.Id) into grouping + from l3 in grouping.DefaultIfEmpty() + where Maybe( + l4, + () => Maybe( + l4.OneToMany_Optional_Inverse4, + () => l4.OneToMany_Optional_Inverse4.Name)) != "Foo" + orderby MaybeScalar( + l2, + () => MaybeScalar( + l2.OneToOne_Optional_FK2, + () => l2.OneToOne_Optional_FK2.Id)) + select new + { + Entity = l4, + Collection = Maybe( + l2, + () => Maybe( + l2.OneToMany_Optional_Self2, + () => l2.OneToMany_Optional_Self2.Where(e => e.Id != 42).ToList())), + Property = Maybe( + l3, + () => Maybe( + l3.OneToOne_Optional_FK_Inverse3, + () => Maybe( + l3.OneToOne_Optional_FK_Inverse3.OneToOne_Required_FK2, + () => l3.OneToOne_Optional_FK_Inverse3.OneToOne_Required_FK2.Name))) + }, elementSorter: e => e.Entity.Id, elementAsserter: (e, a) => { @@ -3104,10 +3104,10 @@ public virtual Task Multiple_SelectMany_with_navigation_and_explicit_DefaultIfEm return AssertQuery( isAsync, ss => from l1 in ss.Set() - from l2 in l1.OneToMany_Optional1 - from l3 in l2.OneToMany_Optional2.Where(l => l.Id > 5).DefaultIfEmpty() - where l3 != null - select l1, + from l2 in l1.OneToMany_Optional1 + from l3 in l2.OneToMany_Optional2.Where(l => l.Id > 5).DefaultIfEmpty() + where l3 != null + select l1, e => e.Id, (e, a) => Assert.Equal(e.Id, a.Id)); } @@ -3119,9 +3119,9 @@ public virtual Task SelectMany_with_navigation_filter_paging_and_explicit_Defaul return AssertQuery( isAsync, ss => from l1 in ss.Set() - from l2 in l1.OneToMany_Required1.Where(l => l.Id > 5).OrderBy(l => l.Id).Take(3).DefaultIfEmpty() - where l2 != null - select l1, + from l2 in l1.OneToMany_Required1.Where(l => l.Id > 5).OrderBy(l => l.Id).Take(3).DefaultIfEmpty() + where l2 != null + select l1, e => e.Id, (e, a) => Assert.Equal(e.Id, a.Id)); } @@ -3223,7 +3223,7 @@ orderby l2i.Id select new { Navigation = l2i.OneToOne_Required_FK_Inverse2, Constant = 7 }).First().Navigation.Name); } - [ConditionalTheory(Skip = "Issue #17756")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Required_navigation_on_a_subquery_with_First_in_predicate(bool isAsync) { @@ -4167,7 +4167,7 @@ where l2.OneToOne_Required_FK2.OneToMany_Optional3 l2.OneToOne_Required_FK2.OneToMany_Optional3, () => l2.OneToOne_Required_FK2.OneToMany_Optional3 .Select(i => i.OneToOne_Optional_PK_Inverse4 == l2.OneToOne_Optional_PK2).Any())) == true - select l2.Name); + select l2.Name); } [ConditionalTheory(Skip = "Issue#16752")] @@ -4263,7 +4263,7 @@ public virtual Task Project_collection_navigation(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set() - select l1.OneToMany_Optional1, + select l1.OneToMany_Optional1, elementSorter: e => e != null ? e.Count : 0, elementAsserter: (e, a) => AssertCollection(e, a)); } @@ -4275,9 +4275,9 @@ public virtual Task Project_collection_navigation_nested(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set() - select l1.OneToOne_Optional_FK1.OneToMany_Optional2, + select l1.OneToOne_Optional_FK1.OneToMany_Optional2, ss => from l1 in ss.Set() - select Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2) ?? new List(), + select Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2) ?? new List(), elementSorter: e => e.Count, elementAsserter: (e, a) => AssertCollection(e, a)); } @@ -4289,9 +4289,9 @@ public virtual Task Project_collection_navigation_nested_with_take(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set() - select l1.OneToOne_Optional_FK1.OneToMany_Optional2.Take(50), + select l1.OneToOne_Optional_FK1.OneToMany_Optional2.Take(50), ss => from l1 in ss.Set() - select Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2.Take(50)) ?? new List(), + select Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2.Take(50)) ?? new List(), elementSorter: e => e?.Count() ?? 0, elementAsserter: (e, a) => AssertCollection(e, a)); } @@ -4303,13 +4303,13 @@ public virtual Task Project_collection_navigation_using_ef_property(bool isAsync return AssertQuery( isAsync, ss => from l1 in ss.Set() - select EF.Property>( - EF.Property( - l1, - "OneToOne_Optional_FK1"), - "OneToMany_Optional2"), + select EF.Property>( + EF.Property( + l1, + "OneToOne_Optional_FK1"), + "OneToMany_Optional2"), ss => from l1 in ss.Set() - select Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2) ?? new List(), + select Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2) ?? new List(), elementSorter: e => e.Count, elementAsserter: (e, a) => AssertCollection(e, a)); } @@ -4321,15 +4321,15 @@ public virtual Task Project_collection_navigation_nested_anonymous(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set() - select new { l1.Id, l1.OneToOne_Optional_FK1.OneToMany_Optional2 }, + select new { l1.Id, l1.OneToOne_Optional_FK1.OneToMany_Optional2 }, ss => from l1 in ss.Set() - select new - { - l1.Id, - OneToMany_Optional2 = Maybe( - l1.OneToOne_Optional_FK1, - () => l1.OneToOne_Optional_FK1.OneToMany_Optional2) ?? new List() - }, + select new + { + l1.Id, + OneToMany_Optional2 = Maybe( + l1.OneToOne_Optional_FK1, + () => l1.OneToOne_Optional_FK1.OneToMany_Optional2) ?? new List() + }, elementSorter: e => e.Id, elementAsserter: (e, a) => { @@ -4345,17 +4345,17 @@ public virtual Task Project_collection_navigation_count(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set() - select new { l1.Id, l1.OneToOne_Optional_FK1.OneToMany_Optional2.Count }, + select new { l1.Id, l1.OneToOne_Optional_FK1.OneToMany_Optional2.Count }, ss => from l1 in ss.Set() - select new - { - l1.Id, - Count = MaybeScalar( - l1.OneToOne_Optional_FK1, - () => MaybeScalar( - l1.OneToOne_Optional_FK1.OneToMany_Optional2, - () => l1.OneToOne_Optional_FK1.OneToMany_Optional2.Count)) ?? 0 - }, + select new + { + l1.Id, + Count = MaybeScalar( + l1.OneToOne_Optional_FK1, + () => MaybeScalar( + l1.OneToOne_Optional_FK1.OneToMany_Optional2, + () => l1.OneToOne_Optional_FK1.OneToMany_Optional2.Count)) ?? 0 + }, elementSorter: e => e.Id); } @@ -4366,8 +4366,8 @@ public virtual Task Project_collection_navigation_composed(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set() - where l1.Id < 3 - select new { l1.Id, collection = l1.OneToMany_Optional1.Where(l2 => l2.Name != "Foo").ToList() }, + where l1.Id < 3 + select new { l1.Id, collection = l1.OneToMany_Optional1.Where(l2 => l2.Name != "Foo").ToList() }, elementSorter: e => e.Id, elementAsserter: (e, a) => { @@ -4383,7 +4383,7 @@ public virtual Task Project_collection_and_root_entity(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set() - select new { l1, l1.OneToMany_Optional1 }, + select new { l1, l1.OneToMany_Optional1 }, elementSorter: e => e.l1.Id, elementAsserter: (e, a) => { @@ -4399,7 +4399,7 @@ public virtual Task Project_collection_and_include(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set().Include(l => l.OneToMany_Optional1) - select new { l1, l1.OneToMany_Optional1 }, + select new { l1, l1.OneToMany_Optional1 }, elementSorter: e => e.l1.Id, elementAsserter: (e, a) => { @@ -4415,13 +4415,13 @@ public virtual Task Project_navigation_and_collection(bool isAsync) return AssertQuery( isAsync, ss => from l1 in ss.Set() - select new { l1.OneToOne_Optional_FK1, l1.OneToOne_Optional_FK1.OneToMany_Optional2 }, + select new { l1.OneToOne_Optional_FK1, l1.OneToOne_Optional_FK1.OneToMany_Optional2 }, ss => from l1 in ss.Set() - select new - { - l1.OneToOne_Optional_FK1, - OneToMany_Optional2 = Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2) ?? new List() - }, + select new + { + l1.OneToOne_Optional_FK1, + OneToMany_Optional2 = Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.OneToMany_Optional2) ?? new List() + }, elementSorter: e => e.OneToOne_Optional_FK1?.Id, elementAsserter: (e, a) => { @@ -4438,7 +4438,7 @@ public virtual Task Include_inside_subquery(bool isAsync) return AssertQuery( isAsync, ss => ss.Set() - .Where(l1 => l1.Id <3) + .Where(l1 => l1.Id < 3) .OrderBy(l1 => l1.Id) .Select(l1 => new { subquery = ss.Set().Include(l => l.OneToMany_Optional2).Where(l => l.Id > 0).ToList() }), assertOrder: true, @@ -5710,20 +5710,26 @@ public virtual Task Null_check_different_structure_does_not_remove_null_checks(b : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3.Name) == "L4 01")); } - [ConditionalFact] - public virtual void Union_over_entities_with_different_nullability() + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Union_over_entities_with_different_nullability(bool isAsync) { - using var ctx = CreateContext(); - - var query = ctx.Set() - .GroupJoin(ctx.Set(), l1 => l1.Id, l2 => l2.Level1_Optional_Id, (l1, l2s) => new { l1, l2s }) - .SelectMany(g => g.l2s.DefaultIfEmpty(), (g, l2) => new { g.l1, l2 }) - .Concat(ctx.Set().GroupJoin(ctx.Set(), l2 => l2.Level1_Optional_Id, l1 => l1.Id, (l2, l1s) => new { l2, l1s }) - .SelectMany(g => g.l1s.DefaultIfEmpty(), (g, l1) => new { l1, g.l2 }) - .Where(e => e.l1.Equals(null))) - .Select(e => e.l1.Id); - - var result = query.ToList(); + return AssertQueryScalar( + isAsync, + ss => ss.Set() + .GroupJoin(ss.Set(), l1 => l1.Id, l2 => l2.Level1_Optional_Id, (l1, l2s) => new { l1, l2s }) + .SelectMany(g => g.l2s.DefaultIfEmpty(), (g, l2) => new { g.l1, l2 }) + .Concat(ss.Set().GroupJoin(ss.Set(), l2 => l2.Level1_Optional_Id, l1 => l1.Id, (l2, l1s) => new { l2, l1s }) + .SelectMany(g => g.l1s.DefaultIfEmpty(), (g, l1) => new { l1, g.l2 }) + .Where(e => e.l1.Equals(null))) + .Select(e => e.l1.Id), + ss => ss.Set() + .GroupJoin(ss.Set(), l1 => l1.Id, l2 => l2.Level1_Optional_Id, (l1, l2s) => new { l1, l2s }) + .SelectMany(g => g.l2s.DefaultIfEmpty(), (g, l2) => new { g.l1, l2 }) + .Concat(ss.Set().GroupJoin(ss.Set(), l2 => l2.Level1_Optional_Id, l1 => l1.Id, (l2, l1s) => new { l2, l1s }) + .SelectMany(g => g.l1s.DefaultIfEmpty(), (g, l1) => new { l1, g.l2 }) + .Where(e => e.l1 == null)) + .Select(e => MaybeScalar(Maybe(e, () => e.l1), () => e.l1.Id) ?? 0)); } [ConditionalTheory] @@ -5749,5 +5755,38 @@ public virtual Task Lift_projection_mapping_when_pushing_down_subquery(bool isAs AssertCollection(e.c2, a.c2, elementSorter: i => i.Id, elementAsserter: (ie, ia) => Assert.Equal(ie.Id, ia.Id)); }); } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Including_reference_navigation_and_projecting_collection_navigation(bool isAsync) + { + return AssertQuery( + isAsync, + ss => ss.Set() + .Include(e => e.OneToOne_Required_FK1) + .ThenInclude(e => e.OneToOne_Optional_FK2) + .Select(e => new Level1 + { + Id = e.Id, + OneToOne_Required_FK1 = e.OneToOne_Required_FK1, + OneToMany_Required1 = e.OneToMany_Required1 + })); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Including_reference_navigation_and_projecting_collection_navigation_2(bool isAsync) + { + return AssertQuery( + isAsync, + ss => ss.Set() + .Include(e => e.OneToOne_Required_FK1) + .Include(e => e.OneToMany_Required1) + .Select(e => new + { + e, + First = e.OneToMany_Required1.OrderByDescending(e => e.Id).FirstOrDefault() + })); + } } } diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs index 4cb29354172..4296d5518e6 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs @@ -169,9 +169,7 @@ public override void Member_pushdown_with_collection_navigation_in_the_middle() { } - public override void Union_over_entities_with_different_nullability() - { - } + public override Task Union_over_entities_with_different_nullability(bool isAsync) => Task.CompletedTask; [ConditionalTheory(Skip = "Issue#16752")] public override Task Include_inside_subquery(bool isAsync) diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index c137abca516..a8e9ce1496b 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -7818,7 +7818,7 @@ private class ComplexParameterInner public Squad Squad { get; set; } } - [ConditionalTheory(Skip = "issue #17852")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Project_entity_and_collection_element(bool isAsync) { @@ -8107,6 +8107,16 @@ public virtual async Task Where_with_enum_flags_parameter(bool isAsync) ss => ss.Set().Where(g => (g.Rank | rank) != rank)); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task FirstOrDefault_navigation_access_entity_equality_in_where_predicate_apply_peneding_selector(bool isAsync) + { + return AssertQuery( + isAsync, + ss => ss.Set() + .Where(f => f.Capital == ss.Set().OrderBy(s => s.Nickname).FirstOrDefault().CityOfBirth)); + } + protected async Task AssertTranslationFailed(Func testCode) { Assert.Contains( diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs index 94c571c43b7..11655d99591 100644 --- a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs @@ -5380,7 +5380,7 @@ public virtual Task Collection_navigation_equal_to_null_for_subquery(bool isAsyn entryCount: 2); } - [ConditionalTheory(Skip = "Issue#17756")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Dependent_to_principal_navigation_equal_to_null_for_subquery(bool isAsync) { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index 632f24495f5..fa49ca2be79 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -2327,14 +2327,17 @@ public override async Task Required_navigation_on_a_subquery_with_First_in_predi await base.Required_navigation_on_a_subquery_with_First_in_predicate(isAsync); AssertSql( - @"SELECT [l2o].[Id], [l2o].[Date], [l2o].[Level1_Optional_Id], [l2o].[Level1_Required_Id], [l2o].[Name], [l2o].[OneToMany_Optional_Inverse2Id], [l2o].[OneToMany_Optional_Self_Inverse2Id], [l2o].[OneToMany_Required_Inverse2Id], [l2o].[OneToMany_Required_Self_Inverse2Id], [l2o].[OneToOne_Optional_PK_Inverse2Id], [l2o].[OneToOne_Optional_Self2Id] -FROM [LevelTwo] AS [l2o] -WHERE [l2o].[Id] = 7", - // - @"SELECT TOP(1) [l2i.OneToOne_Required_FK_Inverse20].[Name] -FROM [LevelTwo] AS [l2i0] -INNER JOIN [LevelOne] AS [l2i.OneToOne_Required_FK_Inverse20] ON [l2i0].[Level1_Required_Id] = [l2i.OneToOne_Required_FK_Inverse20].[Id] -ORDER BY [l2i0].[Id]"); + @"SELECT [l].[Id], [l].[Date], [l].[Level1_Optional_Id], [l].[Level1_Required_Id], [l].[Name], [l].[OneToMany_Optional_Inverse2Id], [l].[OneToMany_Optional_Self_Inverse2Id], [l].[OneToMany_Required_Inverse2Id], [l].[OneToMany_Required_Self_Inverse2Id], [l].[OneToOne_Optional_PK_Inverse2Id], [l].[OneToOne_Optional_Self2Id] +FROM [LevelTwo] AS [l] +WHERE ([l].[Id] = 7) AND ((( + SELECT TOP(1) [l1].[Name] + FROM [LevelTwo] AS [l0] + INNER JOIN [LevelOne] AS [l1] ON [l0].[Level1_Required_Id] = [l1].[Id] + ORDER BY [l0].[Id]) = N'L1 02') AND ( + SELECT TOP(1) [l1].[Name] + FROM [LevelTwo] AS [l0] + INNER JOIN [LevelOne] AS [l1] ON [l0].[Level1_Required_Id] = [l1].[Id] + ORDER BY [l0].[Id]) IS NOT NULL)"); } public override async Task Manually_created_left_join_propagates_nullability_to_navigations(bool isAsync) @@ -4357,9 +4360,9 @@ ELSE CASE END IS NOT NULL"); } - public override void Union_over_entities_with_different_nullability() + public override async Task Union_over_entities_with_different_nullability(bool isAsync) { - base.Union_over_entities_with_different_nullability(); + await base.Union_over_entities_with_different_nullability(isAsync); AssertSql( @"SELECT [t].[Id] @@ -4399,6 +4402,39 @@ public override async Task Lift_projection_mapping_when_pushing_down_subquery(bo ORDER BY [t].[Id], [l1].[Id]"); } + public override async Task Including_reference_navigation_and_projecting_collection_navigation(bool isAsync) + { + await base.Including_reference_navigation_and_projecting_collection_navigation(isAsync); + + AssertSql( + @"SELECT [l].[Id], [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], [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id], [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] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] +LEFT JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[Level2_Optional_Id] +LEFT JOIN [LevelTwo] AS [l2] ON [l].[Id] = [l2].[OneToMany_Required_Inverse2Id] +ORDER BY [l].[Id], [l2].[Id]"); + } + + public override async Task Including_reference_navigation_and_projecting_collection_navigation_2(bool isAsync) + { + await base.Including_reference_navigation_and_projecting_collection_navigation_2(isAsync); + + AssertSql( + @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [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], [t0].[Id], [t0].[Date], [t0].[Level1_Optional_Id], [t0].[Level1_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse2Id], [t0].[OneToMany_Optional_Self_Inverse2Id], [t0].[OneToMany_Required_Inverse2Id], [t0].[OneToMany_Required_Self_Inverse2Id], [t0].[OneToOne_Optional_PK_Inverse2Id], [t0].[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] +FROM [LevelOne] AS [l] +LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] +LEFT JOIN ( + SELECT [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] + FROM ( + SELECT [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], ROW_NUMBER() OVER(PARTITION BY [l1].[OneToMany_Required_Inverse2Id] ORDER BY [l1].[Id] DESC) AS [row] + FROM [LevelTwo] AS [l1] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [l].[Id] = [t0].[OneToMany_Required_Inverse2Id] +LEFT JOIN [LevelTwo] AS [l2] ON [l].[Id] = [l2].[OneToMany_Required_Inverse2Id] +ORDER BY [l].[Id], [l2].[Id]"); + } + private void AssertSql(params string[] expected) { Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 53293cbdc49..0ff4c95e64e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -7499,6 +7499,32 @@ public override async Task Where_with_enum_flags_parameter(bool isAsync) WHERE CAST(0 AS bit) = CAST(1 AS bit)"); } + public override async Task FirstOrDefault_navigation_access_entity_equality_in_where_predicate_apply_peneding_selector(bool isAsync) + { + await base.FirstOrDefault_navigation_access_entity_equality_in_where_predicate_apply_peneding_selector(isAsync); + + AssertSql( + @"SELECT [f].[Id], [f].[CapitalName], [f].[Discriminator], [f].[Name], [f].[CommanderName], [f].[Eradicated] +FROM [Factions] AS [f] +LEFT JOIN [Cities] AS [c] ON [f].[CapitalName] = [c].[Name] +WHERE ([f].[Discriminator] = N'LocustHorde') AND ((([c].[Name] = ( + SELECT TOP(1) [c0].[Name] + FROM [Gears] AS [g] + INNER JOIN [Cities] AS [c0] ON [g].[CityOfBirthName] = [c0].[Name] + WHERE [g].[Discriminator] IN (N'Gear', N'Officer') + ORDER BY [g].[Nickname])) AND ([c].[Name] IS NOT NULL AND ( + SELECT TOP(1) [c0].[Name] + FROM [Gears] AS [g] + INNER JOIN [Cities] AS [c0] ON [g].[CityOfBirthName] = [c0].[Name] + WHERE [g].[Discriminator] IN (N'Gear', N'Officer') + ORDER BY [g].[Nickname]) IS NOT NULL)) OR ([c].[Name] IS NULL AND ( + SELECT TOP(1) [c0].[Name] + FROM [Gears] AS [g] + INNER JOIN [Cities] AS [c0] ON [g].[CityOfBirthName] = [c0].[Name] + WHERE [g].[Discriminator] IN (N'Gear', N'Officer') + ORDER BY [g].[Nickname]) IS NULL))"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs index 84c8615ee35..a2a8e963c18 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs @@ -4623,12 +4623,11 @@ public override async Task Dependent_to_principal_navigation_equal_to_null_for_s @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE ( - SELECT TOP(1) [o.Customer].[CustomerID] + SELECT TOP(1) [c0].[CustomerID] FROM [Orders] AS [o] - LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] - WHERE [c].[CustomerID] = [o].[CustomerID] - ORDER BY [o].[OrderID] -) IS NULL"); + LEFT JOIN [Customers] AS [c0] ON [o].[CustomerID] = [c0].[CustomerID] + WHERE ([c].[CustomerID] = [o].[CustomerID]) AND [o].[CustomerID] IS NOT NULL + ORDER BY [o].[OrderID]) IS NULL"); } public override async Task Collection_navigation_equality_rewrite_for_subquery(bool isAsync)