From 2d8100aefa45c0eca756d207267d2d6ac1c89cea Mon Sep 17 00:00:00 2001 From: Maurycy Markowski Date: Wed, 5 Jul 2017 14:37:34 -0700 Subject: [PATCH] Fix to #8492 - Query: incorrect sql generated for query with owned entities, left join and predicate using inner qsre Problem was that for queries with owned types that were using GroupJoin-SelectMany-DefaultIfEmpty pattern we were not able to find the owned entity type behind the groupjoin subquery qsre. Usually (i.e. in case of navigations) we look for the entity type in the lookup on QueryCompilationContext, but if the groupjoin is created manually then this lookup is not populated with entry for the groupjoin subquery. Fix is to extract JoinClause behind the groupjoin subquery qsre and try to match it against the lookup. --- ...ntityEqualityRewritingExpressionVisitor.cs | 10 ++++++ .../MemberAccessBindingExpressionVisitor.cs | 10 ++++++ ...mplexNavigationsOwnedQuerySqlServerTest.cs | 33 ++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/EFCore/Query/ExpressionVisitors/Internal/EntityEqualityRewritingExpressionVisitor.cs b/src/EFCore/Query/ExpressionVisitors/Internal/EntityEqualityRewritingExpressionVisitor.cs index 3f28455aa0c..cf556376e4f 100644 --- a/src/EFCore/Query/ExpressionVisitors/Internal/EntityEqualityRewritingExpressionVisitor.cs +++ b/src/EFCore/Query/ExpressionVisitors/Internal/EntityEqualityRewritingExpressionVisitor.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; +using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; namespace Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal @@ -123,6 +124,15 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) } } + if (entityType == null + && qsre != null + && qsre.ReferencedQuerySource is AdditionalFromClause additionalFromClause + // ReSharper disable once PatternAlwaysOfType + && additionalFromClause.TryGetFlattenedGroupJoinClause()?.JoinClause is JoinClause joinClause) + { + entityType = _queryCompilationContext.FindEntityType(joinClause); + } + if (entityType != null) { return CreateKeyComparison( diff --git a/src/EFCore/Query/ExpressionVisitors/Internal/MemberAccessBindingExpressionVisitor.cs b/src/EFCore/Query/ExpressionVisitors/Internal/MemberAccessBindingExpressionVisitor.cs index 2d6d0d72270..a040a504307 100644 --- a/src/EFCore/Query/ExpressionVisitors/Internal/MemberAccessBindingExpressionVisitor.cs +++ b/src/EFCore/Query/ExpressionVisitors/Internal/MemberAccessBindingExpressionVisitor.cs @@ -13,6 +13,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query.Expressions.Internal; +using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Storage; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; @@ -425,6 +426,15 @@ var innerExpression entityType = queryCompilationContext.FindEntityType(innerQsre.ReferencedQuerySource); } + if (entityType == null + && innerQsre != null + && innerQsre.ReferencedQuerySource is AdditionalFromClause additionalFromClause + // ReSharper disable once PatternAlwaysOfType + && additionalFromClause.TryGetFlattenedGroupJoinClause()?.JoinClause is JoinClause joinClause) + { + entityType = queryCompilationContext.FindEntityType(joinClause); + } + if (entityType == null) { querySourceReferenceExpression = null; diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsOwnedQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsOwnedQuerySqlServerTest.cs index d105ebf72c9..ae30cfb4128 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsOwnedQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsOwnedQuerySqlServerTest.cs @@ -81,10 +81,41 @@ public override void Nested_group_join_with_take() base.Nested_group_join_with_take(); } - [ConditionalFact(Skip = "issue #8492")] + [ConditionalFact] public override void Explicit_GroupJoin_in_subquery_with_unrelated_projection2() { base.Explicit_GroupJoin_in_subquery_with_unrelated_projection2(); + + AssertSql( + @"SELECT [t1].[Id] +FROM ( + SELECT DISTINCT [l1].* + FROM [Level1] AS [l1] + LEFT JOIN ( + SELECT [t].* + FROM [Level1] AS [t] + WHERE [t].[Id] IS NOT NULL + ) AS [t0] ON [l1].[Id] = [t0].[OneToOne_Required_PK_Level1_Optional_Id] + WHERE ([t0].[OneToOne_Required_PK_Name] <> N'Foo') OR [t0].[OneToOne_Required_PK_Name] IS NULL +) AS [t1]"); + } + + [ConditionalFact] + public override void Result_operator_nav_prop_reference_optional_via_DefaultIfEmpty() + { + base.Result_operator_nav_prop_reference_optional_via_DefaultIfEmpty(); + + AssertSql( + @"SELECT SUM(CASE + WHEN [t0].[Id] IS NULL + THEN 0 ELSE [t0].[OneToOne_Required_PK_Level1_Required_Id] +END) +FROM [Level1] AS [l1] +LEFT JOIN ( + SELECT [t].* + FROM [Level1] AS [t] + WHERE [t].[Id] IS NOT NULL +) AS [t0] ON [l1].[Id] = [t0].[OneToOne_Required_PK_Level1_Optional_Id]"); } private void AssertSql(params string[] expected)