diff --git a/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs b/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs index 659fd7cdb2d..279fd693946 100644 --- a/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs @@ -675,6 +675,9 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp private sealed class SelectManyVerifyingExpressionVisitor : ExpressionVisitor { + private static readonly bool UseOldBehavior30575 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue30575", out var enabled30575) && enabled30575; + private readonly List _allowedParameters = new(); private readonly ISet _allowedMethods = new HashSet { nameof(Queryable.Where), nameof(Queryable.AsQueryable) }; @@ -774,9 +777,20 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp protected override Expression VisitParameter(ParameterExpression parameterExpression) { - if (_allowedParameters.Contains(parameterExpression)) + if (!UseOldBehavior30575) { - return parameterExpression; + if (_allowedParameters.Contains(parameterExpression) + || parameterExpression.Name?.StartsWith(QueryCompilationContext.QueryParameterPrefix, StringComparison.Ordinal) == true) + { + return parameterExpression; + } + } + else + { + if (_allowedParameters.Contains(parameterExpression)) + { + return parameterExpression; + } } if (parameterExpression == _rootParameter) diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs index cea1656b7f6..3a0af8d4125 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs @@ -3785,4 +3785,139 @@ public virtual Task Prune_does_not_throw_null_ref(bool async) select l2.Level1_Required_Id).DefaultIfEmpty() from l1 in ss.Set().Where(x => x.Id != ids) select l1); + + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure(bool async) + { + var prm = 10; + + return AssertQuery( + async, + ss => from l1 in ss.Set() + join l2 in ss.Set() on l1.Id equals l2.Level1_Optional_Id into grouping + from l2 in grouping.Where(x => x.Id != prm).DefaultIfEmpty() + select new { Id1 = l1.Id, Id2 = (int?)l2.Id }, + elementSorter: e => (e.Id1, e.Id2), + elementAsserter: (e, a) => + { + Assert.Equal(e.Id1, a.Id1); + Assert.Equal(e.Id2, a.Id2); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupJoin_SelectMany_with_predicate_using_closure(bool async) + { + var prm = 10; + + return AssertQuery( + async, + ss => from l1 in ss.Set() + join l2 in ss.Set() on l1.Id equals l2.Level1_Optional_Id into grouping + from l2 in grouping.Where(x => x.Id != prm) + select new { Id1 = l1.Id, Id2 = l2.Id }, + elementSorter: e => (e.Id1, e.Id2), + elementAsserter: (e, a) => + { + Assert.Equal(e.Id1, a.Id1); + Assert.Equal(e.Id2, a.Id2); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested(bool async) + { + var prm1 = 10; + var prm2 = 20; + + return AssertQuery( + async, + ss => from l1 in ss.Set() + join l2 in ss.Set() on l1.Id equals l2.Level1_Optional_Id into grouping1 + from l2 in grouping1.Where(x => x.Id != prm1).DefaultIfEmpty() + join l3 in ss.Set() on l2.Id equals l3.Level2_Optional_Id into grouping2 + from l3 in grouping2.Where(x => x.Id != prm2).DefaultIfEmpty() + select new { Id1 = l1.Id, Id2 = (int?)l2.Id, Id3 = (int?)l3.Id }, + elementSorter: e => (e.Id1, e.Id2, e.Id3), + elementAsserter: (e, a) => + { + Assert.Equal(e.Id1, a.Id1); + Assert.Equal(e.Id2, a.Id2); + Assert.Equal(e.Id3, a.Id3); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupJoin_SelectMany_with_predicate_using_closure_nested(bool async) + { + var prm1 = 10; + var prm2 = 20; + + return AssertQuery( + async, + ss => from l1 in ss.Set() + join l2 in ss.Set() on l1.Id equals l2.Level1_Optional_Id into grouping1 + from l2 in grouping1.Where(x => x.Id != prm1) + join l3 in ss.Set() on l2.Id equals l3.Level2_Optional_Id into grouping2 + from l3 in grouping2.Where(x => x.Id != prm2) + select new { Id1 = l1.Id, Id2 = l2.Id, Id3 = l3.Id }, + elementSorter: e => (e.Id1, e.Id2, e.Id3), + elementAsserter: (e, a) => + { + Assert.Equal(e.Id1, a.Id1); + Assert.Equal(e.Id2, a.Id2); + Assert.Equal(e.Id3, a.Id3); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested_same_param(bool async) + { + var prm = 10; + + return AssertQuery( + async, + ss => from l1 in ss.Set() + join l2 in ss.Set() on l1.Id equals l2.Level1_Optional_Id into grouping1 + from l2 in grouping1.Where(x => x.Id != prm).DefaultIfEmpty() + join l3 in ss.Set() on l2.Id equals l3.Level2_Optional_Id into grouping2 + from l3 in grouping2.Where(x => x.Id != prm).DefaultIfEmpty() + select new { Id1 = l1.Id, Id2 = (int?)l2.Id, Id3 = (int?)l3.Id }, + elementSorter: e => (e.Id1, e.Id2, e.Id3), + elementAsserter: (e, a) => + { + Assert.Equal(e.Id1, a.Id1); + Assert.Equal(e.Id2, a.Id2); + Assert.Equal(e.Id3, a.Id3); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupJoin_SelectMany_with_predicate_using_closure_nested_same_param(bool async) + { + var prm = 10; + + return AssertQuery( + async, + ss => from l1 in ss.Set() + join l2 in ss.Set() on l1.Id equals l2.Level1_Optional_Id into grouping1 + from l2 in grouping1.Where(x => x.Id != prm) + join l3 in ss.Set() on l2.Id equals l3.Level2_Optional_Id into grouping2 + from l3 in grouping2.Where(x => x.Id != prm) + select new { Id1 = l1.Id, Id2 = l2.Id, Id3 = l3.Id }, + elementSorter: e => (e.Id1, e.Id2, e.Id3), + elementAsserter: (e, a) => + { + Assert.Equal(e.Id1, a.Id1); + Assert.Equal(e.Id2, a.Id2); + Assert.Equal(e.Id3, a.Id3); + }); + } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index 85e32974927..414de3d9fb3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -4576,6 +4576,136 @@ public override async Task Multiple_required_navigation_with_EF_Property_Include AssertSql(); } + public override async Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure(bool async) + { + await base.GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure(async); + + AssertSql( +""" +@__prm_0='10' + +SELECT [l].[Id] AS [Id1], [t].[Id] AS [Id2] +FROM [LevelOne] AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[Level1_Optional_Id] + FROM [LevelTwo] AS [l0] + WHERE [l0].[Id] <> @__prm_0 +) AS [t] ON [l].[Id] = [t].[Level1_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_with_predicate_using_closure(bool async) + { + await base.GroupJoin_SelectMany_with_predicate_using_closure(async); + + AssertSql( +""" +@__prm_0='10' + +SELECT [l].[Id] AS [Id1], [t].[Id] AS [Id2] +FROM [LevelOne] AS [l] +INNER JOIN ( + SELECT [l0].[Id], [l0].[Level1_Optional_Id] + FROM [LevelTwo] AS [l0] + WHERE [l0].[Id] <> @__prm_0 +) AS [t] ON [l].[Id] = [t].[Level1_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested(bool async) + { + await base.GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested(async); + + AssertSql( +""" +@__prm1_0='10' +@__prm2_1='20' + +SELECT [l].[Id] AS [Id1], [t].[Id] AS [Id2], [t0].[Id] AS [Id3] +FROM [LevelOne] AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[Level1_Optional_Id] + FROM [LevelTwo] AS [l0] + WHERE [l0].[Id] <> @__prm1_0 +) AS [t] ON [l].[Id] = [t].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id] + FROM [LevelThree] AS [l1] + WHERE [l1].[Id] <> @__prm2_1 +) AS [t0] ON [t].[Id] = [t0].[Level2_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_with_predicate_using_closure_nested(bool async) + { + await base.GroupJoin_SelectMany_with_predicate_using_closure_nested(async); + + AssertSql( +""" +@__prm1_0='10' +@__prm2_1='20' + +SELECT [l].[Id] AS [Id1], [t].[Id] AS [Id2], [t0].[Id] AS [Id3] +FROM [LevelOne] AS [l] +INNER JOIN ( + SELECT [l0].[Id], [l0].[Level1_Optional_Id] + FROM [LevelTwo] AS [l0] + WHERE [l0].[Id] <> @__prm1_0 +) AS [t] ON [l].[Id] = [t].[Level1_Optional_Id] +INNER JOIN ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id] + FROM [LevelThree] AS [l1] + WHERE [l1].[Id] <> @__prm2_1 +) AS [t0] ON [t].[Id] = [t0].[Level2_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested_same_param(bool async) + { + await base.GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested_same_param(async); + + AssertSql( +""" +@__prm_0='10' + +SELECT [l].[Id] AS [Id1], [t].[Id] AS [Id2], [t0].[Id] AS [Id3] +FROM [LevelOne] AS [l] +LEFT JOIN ( + SELECT [l0].[Id], [l0].[Level1_Optional_Id] + FROM [LevelTwo] AS [l0] + WHERE [l0].[Id] <> @__prm_0 +) AS [t] ON [l].[Id] = [t].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id] + FROM [LevelThree] AS [l1] + WHERE [l1].[Id] <> @__prm_0 +) AS [t0] ON [t].[Id] = [t0].[Level2_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_with_predicate_using_closure_nested_same_param(bool async) + { + await base.GroupJoin_SelectMany_with_predicate_using_closure_nested_same_param(async); + + AssertSql( +""" +@__prm_0='10' + +SELECT [l].[Id] AS [Id1], [t].[Id] AS [Id2], [t0].[Id] AS [Id3] +FROM [LevelOne] AS [l] +INNER JOIN ( + SELECT [l0].[Id], [l0].[Level1_Optional_Id] + FROM [LevelTwo] AS [l0] + WHERE [l0].[Id] <> @__prm_0 +) AS [t] ON [l].[Id] = [t].[Level1_Optional_Id] +INNER JOIN ( + SELECT [l1].[Id], [l1].[Level2_Optional_Id] + FROM [LevelThree] AS [l1] + WHERE [l1].[Id] <> @__prm_0 +) AS [t0] ON [t].[Id] = [t0].[Level2_Optional_Id] +"""); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs index ab52e848f35..faf0ea49e10 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs @@ -7857,6 +7857,310 @@ public override async Task Multiple_required_navigation_with_EF_Property_Include AssertSql(); } + public override async Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure(bool async) + { + await base.GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure(async); + + AssertSql( +""" +@__prm_0='10' + +SELECT [l].[Id] AS [Id1], CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END AS [Id2] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [t].[Id] AS [Id0], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l0] + LEFT JOIN ( + SELECT [l1].[Id], [l1].[OneToOne_Required_PK_Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l1] + WHERE ([l1].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l1].[Level1_Required_Id] IS NOT NULL) AND ([l1].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t] ON [l0].[Id] = CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END + WHERE ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END <> @__prm_0 OR (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END IS NULL)) +) AS [t0] ON [l].[Id] = [t0].[Level1_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_with_predicate_using_closure(bool async) + { + await base.GroupJoin_SelectMany_with_predicate_using_closure(async); + + AssertSql( +""" +@__prm_0='10' + +SELECT [l].[Id] AS [Id1], CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END AS [Id2] +FROM [Level1] AS [l] +INNER JOIN ( + SELECT [t].[Id] AS [Id0], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l0] + LEFT JOIN ( + SELECT [l1].[Id], [l1].[OneToOne_Required_PK_Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l1] + WHERE ([l1].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l1].[Level1_Required_Id] IS NOT NULL) AND ([l1].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t] ON [l0].[Id] = CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END + WHERE ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END <> @__prm_0 OR (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END IS NULL)) +) AS [t0] ON [l].[Id] = [t0].[Level1_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested(bool async) + { + await base.GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested(async); + + AssertSql( +""" +@__prm1_0='10' +@__prm2_1='20' + +SELECT [l].[Id] AS [Id1], CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END AS [Id2], CASE + WHEN ([t1].[Level2_Required_Id] IS NOT NULL) AND ([t1].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t1].[Id1] +END AS [Id3] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [t].[Id] AS [Id0], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l0] + LEFT JOIN ( + SELECT [l1].[Id], [l1].[OneToOne_Required_PK_Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l1] + WHERE ([l1].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l1].[Level1_Required_Id] IS NOT NULL) AND ([l1].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t] ON [l0].[Id] = CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END + WHERE ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END <> @__prm1_0 OR (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END IS NULL)) +) AS [t0] ON [l].[Id] = [t0].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [t3].[Id] AS [Id1], [t3].[Level2_Optional_Id], [t3].[Level2_Required_Id], [t3].[OneToMany_Required_Inverse3Id] + FROM [Level1] AS [l2] + LEFT JOIN ( + SELECT [l3].[Id], [l3].[OneToOne_Required_PK_Date], [l3].[Level1_Required_Id], [l3].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l3] + WHERE ([l3].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l3].[Level1_Required_Id] IS NOT NULL) AND ([l3].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t2] ON [l2].[Id] = CASE + WHEN ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t2].[Id] + END + LEFT JOIN ( + SELECT [l4].[Id], [l4].[Level2_Optional_Id], [l4].[Level2_Required_Id], [l4].[OneToMany_Required_Inverse3Id] + FROM [Level1] AS [l4] + WHERE ([l4].[Level2_Required_Id] IS NOT NULL) AND ([l4].[OneToMany_Required_Inverse3Id] IS NOT NULL) + ) AS [t3] ON CASE + WHEN ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t2].[Id] + END = CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END + WHERE ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) AND (CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END <> @__prm2_1 OR (CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END IS NULL)) +) AS [t1] ON CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END = [t1].[Level2_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_with_predicate_using_closure_nested(bool async) + { + await base.GroupJoin_SelectMany_with_predicate_using_closure_nested(async); + + AssertSql( +""" +@__prm1_0='10' +@__prm2_1='20' + +SELECT [l].[Id] AS [Id1], CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END AS [Id2], CASE + WHEN ([t1].[Level2_Required_Id] IS NOT NULL) AND ([t1].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t1].[Id1] +END AS [Id3] +FROM [Level1] AS [l] +INNER JOIN ( + SELECT [t].[Id] AS [Id0], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l0] + LEFT JOIN ( + SELECT [l1].[Id], [l1].[OneToOne_Required_PK_Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l1] + WHERE ([l1].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l1].[Level1_Required_Id] IS NOT NULL) AND ([l1].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t] ON [l0].[Id] = CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END + WHERE ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END <> @__prm1_0 OR (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END IS NULL)) +) AS [t0] ON [l].[Id] = [t0].[Level1_Optional_Id] +INNER JOIN ( + SELECT [t3].[Id] AS [Id1], [t3].[Level2_Optional_Id], [t3].[Level2_Required_Id], [t3].[OneToMany_Required_Inverse3Id] + FROM [Level1] AS [l2] + LEFT JOIN ( + SELECT [l3].[Id], [l3].[OneToOne_Required_PK_Date], [l3].[Level1_Required_Id], [l3].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l3] + WHERE ([l3].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l3].[Level1_Required_Id] IS NOT NULL) AND ([l3].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t2] ON [l2].[Id] = CASE + WHEN ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t2].[Id] + END + LEFT JOIN ( + SELECT [l4].[Id], [l4].[Level2_Optional_Id], [l4].[Level2_Required_Id], [l4].[OneToMany_Required_Inverse3Id] + FROM [Level1] AS [l4] + WHERE ([l4].[Level2_Required_Id] IS NOT NULL) AND ([l4].[OneToMany_Required_Inverse3Id] IS NOT NULL) + ) AS [t3] ON CASE + WHEN ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t2].[Id] + END = CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END + WHERE ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) AND (CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END <> @__prm2_1 OR (CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END IS NULL)) +) AS [t1] ON CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END = [t1].[Level2_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested_same_param(bool async) + { + await base.GroupJoin_SelectMany_DefaultIfEmpty_with_predicate_using_closure_nested_same_param(async); + + AssertSql( +""" +@__prm_0='10' + +SELECT [l].[Id] AS [Id1], CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END AS [Id2], CASE + WHEN ([t1].[Level2_Required_Id] IS NOT NULL) AND ([t1].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t1].[Id1] +END AS [Id3] +FROM [Level1] AS [l] +LEFT JOIN ( + SELECT [t].[Id] AS [Id0], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l0] + LEFT JOIN ( + SELECT [l1].[Id], [l1].[OneToOne_Required_PK_Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l1] + WHERE ([l1].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l1].[Level1_Required_Id] IS NOT NULL) AND ([l1].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t] ON [l0].[Id] = CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END + WHERE ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END <> @__prm_0 OR (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END IS NULL)) +) AS [t0] ON [l].[Id] = [t0].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [t3].[Id] AS [Id1], [t3].[Level2_Optional_Id], [t3].[Level2_Required_Id], [t3].[OneToMany_Required_Inverse3Id] + FROM [Level1] AS [l2] + LEFT JOIN ( + SELECT [l3].[Id], [l3].[OneToOne_Required_PK_Date], [l3].[Level1_Required_Id], [l3].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l3] + WHERE ([l3].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l3].[Level1_Required_Id] IS NOT NULL) AND ([l3].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t2] ON [l2].[Id] = CASE + WHEN ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t2].[Id] + END + LEFT JOIN ( + SELECT [l4].[Id], [l4].[Level2_Optional_Id], [l4].[Level2_Required_Id], [l4].[OneToMany_Required_Inverse3Id] + FROM [Level1] AS [l4] + WHERE ([l4].[Level2_Required_Id] IS NOT NULL) AND ([l4].[OneToMany_Required_Inverse3Id] IS NOT NULL) + ) AS [t3] ON CASE + WHEN ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t2].[Id] + END = CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END + WHERE ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) AND (CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END <> @__prm_0 OR (CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END IS NULL)) +) AS [t1] ON CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END = [t1].[Level2_Optional_Id] +"""); + } + + public override async Task GroupJoin_SelectMany_with_predicate_using_closure_nested_same_param(bool async) + { + await base.GroupJoin_SelectMany_with_predicate_using_closure_nested_same_param(async); + + AssertSql( +""" +@__prm_0='10' + +SELECT [l].[Id] AS [Id1], CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END AS [Id2], CASE + WHEN ([t1].[Level2_Required_Id] IS NOT NULL) AND ([t1].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t1].[Id1] +END AS [Id3] +FROM [Level1] AS [l] +INNER JOIN ( + SELECT [t].[Id] AS [Id0], [t].[OneToOne_Required_PK_Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l0] + LEFT JOIN ( + SELECT [l1].[Id], [l1].[OneToOne_Required_PK_Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l1] + WHERE ([l1].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l1].[Level1_Required_Id] IS NOT NULL) AND ([l1].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t] ON [l0].[Id] = CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END + WHERE ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END <> @__prm_0 OR (CASE + WHEN ([t].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t].[Level1_Required_Id] IS NOT NULL) AND ([t].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t].[Id] + END IS NULL)) +) AS [t0] ON [l].[Id] = [t0].[Level1_Optional_Id] +INNER JOIN ( + SELECT [t3].[Id] AS [Id1], [t3].[Level2_Optional_Id], [t3].[Level2_Required_Id], [t3].[OneToMany_Required_Inverse3Id] + FROM [Level1] AS [l2] + LEFT JOIN ( + SELECT [l3].[Id], [l3].[OneToOne_Required_PK_Date], [l3].[Level1_Required_Id], [l3].[OneToMany_Required_Inverse2Id] + FROM [Level1] AS [l3] + WHERE ([l3].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([l3].[Level1_Required_Id] IS NOT NULL) AND ([l3].[OneToMany_Required_Inverse2Id] IS NOT NULL) + ) AS [t2] ON [l2].[Id] = CASE + WHEN ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t2].[Id] + END + LEFT JOIN ( + SELECT [l4].[Id], [l4].[Level2_Optional_Id], [l4].[Level2_Required_Id], [l4].[OneToMany_Required_Inverse3Id] + FROM [Level1] AS [l4] + WHERE ([l4].[Level2_Required_Id] IS NOT NULL) AND ([l4].[OneToMany_Required_Inverse3Id] IS NOT NULL) + ) AS [t3] ON CASE + WHEN ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t2].[Id] + END = CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END + WHERE ([t2].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t2].[Level1_Required_Id] IS NOT NULL) AND ([t2].[OneToMany_Required_Inverse2Id] IS NOT NULL) AND ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) AND (CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END <> @__prm_0 OR (CASE + WHEN ([t3].[Level2_Required_Id] IS NOT NULL) AND ([t3].[OneToMany_Required_Inverse3Id] IS NOT NULL) THEN [t3].[Id] + END IS NULL)) +) AS [t1] ON CASE + WHEN ([t0].[OneToOne_Required_PK_Date] IS NOT NULL) AND ([t0].[Level1_Required_Id] IS NOT NULL) AND ([t0].[OneToMany_Required_Inverse2Id] IS NOT NULL) THEN [t0].[Id0] +END = [t1].[Level2_Optional_Id] +"""); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); }