diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs index 09cf4e67944..e2680bdbbe9 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs @@ -5823,7 +5823,7 @@ orderby l4.Id } } - [ConditionalTheory(Skip = "issue #15798")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Member_pushdown_with_multiple_collections(bool isAsync) { diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs index 443e942fe13..99f80522ce2 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs @@ -150,6 +150,12 @@ public override Task Include_collection_with_multiple_orderbys_property(bool isA return base.Include_collection_with_multiple_orderbys_property(isAsync); } + [ConditionalTheory(Skip = "Issue#17803")] + public override Task Member_pushdown_with_multiple_collections(bool isAsync) + { + return base.Member_pushdown_with_multiple_collections(isAsync); + } + // Cannot create DbSet for Level2 public override void Join_with_navigations_in_the_result_selector2() { diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index 6e7f8d6e26c..af5cc9a6c54 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -7611,6 +7611,121 @@ public virtual Task Null_checks_in_correlated_predicate_are_correctly_translated }); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task SelectMany_Where_DefaultIfEmpty_with_navigation_in_the_collection_selector(bool isAsync) + { + var isAutomatic = true; + + return AssertQuery( + isAsync, + gs => from g in gs + from w in g.Weapons.Where(ww => ww.IsAutomatic == isAutomatic).DefaultIfEmpty() + select new + { + g.Nickname, + g.FullName, + Collection = w != null + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Join_with_inner_being_a_subquery_projecting_single_property(bool isAsync) + { + return AssertQuery( + isAsync, + gs => from g in gs + join inner in ( + from g2 in gs + select g2.Nickname + ) on g.Nickname equals inner + select g); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Join_with_inner_being_a_subquery_projecting_anonymous_type_with_single_property(bool isAsync) + { + return AssertQuery( + isAsync, + gs => from g in gs + join inner in ( + from g2 in gs + select new { g2.Nickname } + ) on g.Nickname equals inner.Nickname + select g); + } + + [ConditionalTheory(Skip = "issue #17475")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Navigation_based_on_complex_expression1(bool isAsync) + { + return AssertQuery( + isAsync, + fs => fs.Where(f => f is LocustHorde ? (f as LocustHorde).Commander != null : false)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Navigation_based_on_complex_expression2(bool isAsync) + { + return AssertQuery( + isAsync, + fs => fs.Where(f => f is LocustHorde).Where(f => ((LocustHorde)f).Commander != null)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Navigation_based_on_complex_expression3(bool isAsync) + { + return AssertQuery( + isAsync, + fs => fs.Where(f => f is LocustHorde).Select(f => ((LocustHorde)f).Commander)); + } + + [ConditionalTheory(Skip = "issue #17782")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Navigation_based_on_complex_expression4(bool isAsync) + { + return AssertQuery( + isAsync, + (fs, lls) => from lc1 in fs.Select(f => (f is LocustHorde) ? ((LocustHorde)f).Commander : null) + from lc2 in lls.OfType() + select (lc1 ?? lc2).DefeatedBy); + } + + [ConditionalTheory(Skip = "issue #17782")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Navigation_based_on_complex_expression5(bool isAsync) + { + return AssertQuery( + isAsync, + (fs, lls) => from lc1 in fs.OfType().Select(lh => lh.Commander) + join lc2 in lls.OfType() on true equals true + select (lc1 ?? lc2).DefeatedBy); + } + + [ConditionalTheory(Skip = "issue #17782")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Navigation_based_on_complex_expression6(bool isAsync) + { + return AssertQuery( + isAsync, + (fs, lls) => from lc1 in fs.OfType().Select(lh => lh.Commander) + join lc2 in lls.OfType() on true equals true + select (lc1.Name == "Queen Myrrah" ? lc1 : lc2).DefeatedBy); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Select_as_operator(bool isAsync) + { + return AssertQuery( + isAsync, + lls => lls.Select(ll => ll as LocustCommander)); + } + protected GearsOfWarContext CreateContext() => Fixture.CreateContext(); protected virtual void ClearLog() diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Select.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Select.cs index d8549ada1d8..5f1fea34f81 100644 --- a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Select.cs +++ b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Select.cs @@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.TestModels.Northwind; using Xunit; +using Xunit.Sdk; // ReSharper disable StringStartsWithIsCultureSpecific // ReSharper disable InconsistentNaming @@ -1308,5 +1309,56 @@ public virtual async Task Filtered_collection_projection_with_to_list_is_tracked Assert.Equal(6, context.ChangeTracker.Entries().Count()); } } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task SelectMany_with_collection_being_correlated_subquery_which_references_inner_and_outer_entity(bool isAsync) + { + return AssertQuery( + isAsync, + cs => cs.SelectMany(c => c.Orders.Select(o => new { OrderProperty = o.CustomerID, CustomerProperty = c.CustomerID }))); + } + + [ConditionalTheory(Skip = "issue #17763")] + [MemberData(nameof(IsAsyncData))] + public virtual Task SelectMany_with_collection_being_correlated_subquery_which_references_non_mapped_properties_from_inner_and_outer_entity(bool isAsync) + { + return AssertQuery( + isAsync, + cs => cs.SelectMany(c => c.Orders.Select(o => new { OrderProperty = o.ShipName, CustomerProperty = c.ContactName }))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Select_with_complex_expression_that_can_be_funcletized(bool isAsync) + { + return AssertQueryScalar( + isAsync, + cs => cs.Where(c => c.CustomerID == "ALFKI").Select(c => c.ContactName.IndexOf("")), + assertOrder: true); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Select_chained_entity_navigation_doesnt_materialize_intermittent_entities(bool isAsync) + { + return AssertQuery( + isAsync, + os => os.OrderBy(o => o.OrderID).Select(o => o.Customer.Orders), + elementAsserter: CollectionAsserter(), + assertOrder: true, + entryCount: 830); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Select_entity_compared_to_null(bool isAsync) + { + return AssertQueryScalar( + isAsync, + os => from o in os + where o.CustomerID == "ALFKI" + select o.Customer == null); + } } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index 887d7a9ea7f..9d7a409f1fb 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -4650,7 +4650,28 @@ public override async Task Member_pushdown_with_multiple_collections(bool isAsyn await base.Member_pushdown_with_multiple_collections(isAsync); AssertSql( - @""); + @"SELECT ( + SELECT TOP(1) [l].[Name] + FROM [LevelThree] AS [l] + WHERE ( + SELECT TOP(1) [l0].[Id] + FROM [LevelTwo] AS [l0] + WHERE ([l2].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND [l0].[OneToMany_Optional_Inverse2Id] IS NOT NULL + ORDER BY [l0].[Id]) IS NOT NULL AND (((( + SELECT TOP(1) [l1].[Id] + FROM [LevelTwo] AS [l1] + WHERE ([l2].[Id] = [l1].[OneToMany_Optional_Inverse2Id]) AND [l1].[OneToMany_Optional_Inverse2Id] IS NOT NULL + ORDER BY [l1].[Id]) = [l].[OneToMany_Optional_Inverse3Id]) AND (( + SELECT TOP(1) [l1].[Id] + FROM [LevelTwo] AS [l1] + WHERE ([l2].[Id] = [l1].[OneToMany_Optional_Inverse2Id]) AND [l1].[OneToMany_Optional_Inverse2Id] IS NOT NULL + ORDER BY [l1].[Id]) IS NOT NULL AND [l].[OneToMany_Optional_Inverse3Id] IS NOT NULL)) OR (( + SELECT TOP(1) [l1].[Id] + FROM [LevelTwo] AS [l1] + WHERE ([l2].[Id] = [l1].[OneToMany_Optional_Inverse2Id]) AND [l1].[OneToMany_Optional_Inverse2Id] IS NOT NULL + ORDER BY [l1].[Id]) IS NULL AND [l].[OneToMany_Optional_Inverse3Id] IS NULL)) + ORDER BY [l].[Id]) +FROM [LevelOne] AS [l2]"); } public override async Task Null_check_removal_applied_recursively(bool isAsync) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index e1a0016e5bd..88bf0defa96 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -7008,6 +7008,128 @@ WHERE [g].[Discriminator] IN (N'Gear', N'Officer') ORDER BY [t].[Id], [t0].[Nickname], [t0].[SquadId]"); } + public override async Task SelectMany_Where_DefaultIfEmpty_with_navigation_in_the_collection_selector(bool isAsync) + { + await base.SelectMany_Where_DefaultIfEmpty_with_navigation_in_the_collection_selector(isAsync); + + AssertSql( + @"@__isAutomatic_0='True' + +SELECT [g].[Nickname], [g].[FullName], CASE + WHEN [t].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [Collection] +FROM [Gears] AS [g] +LEFT JOIN ( + SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] + FROM [Weapons] AS [w] + WHERE ([w].[IsAutomatic] = @__isAutomatic_0) AND @__isAutomatic_0 IS NOT NULL +) AS [t] ON [g].[FullName] = [t].[OwnerFullName] +WHERE [g].[Discriminator] IN (N'Gear', N'Officer')"); + } + + public override async Task Join_with_inner_being_a_subquery_projecting_single_property(bool isAsync) + { + await base.Join_with_inner_being_a_subquery_projecting_single_property(isAsync); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] +FROM [Gears] AS [g] +INNER JOIN ( + SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOrBirthName], [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 [t] ON [g].[Nickname] = [t].[Nickname] +WHERE [g].[Discriminator] IN (N'Gear', N'Officer')"); + } + + public override async Task Join_with_inner_being_a_subquery_projecting_anonymous_type_with_single_property(bool isAsync) + { + await base.Join_with_inner_being_a_subquery_projecting_anonymous_type_with_single_property(isAsync); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] +FROM [Gears] AS [g] +INNER JOIN ( + SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOrBirthName], [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 [t] ON [g].[Nickname] = [t].[Nickname] +WHERE [g].[Discriminator] IN (N'Gear', N'Officer')"); + } + + public override async Task Navigation_based_on_complex_expression1(bool isAsync) + { + await base.Navigation_based_on_complex_expression1(isAsync); + + AssertSql( + @""); + } + + public override async Task Navigation_based_on_complex_expression2(bool isAsync) + { + await base.Navigation_based_on_complex_expression2(isAsync); + + AssertSql( + @"SELECT [f].[Id], [f].[CapitalName], [f].[Discriminator], [f].[Name], [f].[CommanderName], [f].[Eradicated] +FROM [Factions] AS [f] +LEFT JOIN ( + SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId] + FROM [LocustLeaders] AS [l] + WHERE [l].[Discriminator] = N'LocustCommander' +) AS [t] ON [f].[CommanderName] = [t].[Name] +WHERE (([f].[Discriminator] = N'LocustHorde') AND ([f].[Discriminator] = N'LocustHorde')) AND [t].[Name] IS NOT NULL"); + } + + public override async Task Navigation_based_on_complex_expression3(bool isAsync) + { + await base.Navigation_based_on_complex_expression3(isAsync); + + AssertSql( + @"SELECT [t].[Name], [t].[Discriminator], [t].[LocustHordeId], [t].[ThreatLevel], [t].[DefeatedByNickname], [t].[DefeatedBySquadId], [t].[HighCommandId] +FROM [Factions] AS [f] +LEFT JOIN ( + SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId] + FROM [LocustLeaders] AS [l] + WHERE [l].[Discriminator] = N'LocustCommander' +) AS [t] ON [f].[CommanderName] = [t].[Name] +WHERE ([f].[Discriminator] = N'LocustHorde') AND ([f].[Discriminator] = N'LocustHorde')"); + } + + public override async Task Navigation_based_on_complex_expression4(bool isAsync) + { + await base.Navigation_based_on_complex_expression4(isAsync); + + AssertSql( + @""); + } + + public override async Task Navigation_based_on_complex_expression5(bool isAsync) + { + await base.Navigation_based_on_complex_expression5(isAsync); + + AssertSql( + @""); + } + + public override async Task Navigation_based_on_complex_expression6(bool isAsync) + { + await base.Navigation_based_on_complex_expression6(isAsync); + + AssertSql( + @""); + } + + public override async Task Select_as_operator(bool isAsync) + { + await base.Select_as_operator(isAsync); + + AssertSql( + @"SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId] +FROM [LocustLeaders] AS [l] +WHERE [l].[Discriminator] IN (N'LocustLeader', N'LocustCommander')"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Select.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Select.cs index 07057d77214..1287d4d0bc6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Select.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Select.cs @@ -1065,5 +1065,66 @@ WHERE [o].[OrderID] > 11000 WHERE [c].[CustomerID] LIKE N'A%' ORDER BY [c].[CustomerID], [t].[OrderID]"); } + + public override async Task SelectMany_with_collection_being_correlated_subquery_which_references_inner_and_outer_entity(bool isAsync) + { + await base.SelectMany_with_collection_being_correlated_subquery_which_references_inner_and_outer_entity(isAsync); + + AssertSql( + @"SELECT [t].[CustomerID] AS [OrderProperty], [t].[CustomerID0] AS [CustomerProperty] +FROM [Customers] AS [c] +CROSS APPLY ( + SELECT [o].[CustomerID], [c].[CustomerID] AS [CustomerID0], [o].[OrderID] + FROM [Orders] AS [o] + WHERE ([c].[CustomerID] = [o].[CustomerID]) AND [o].[CustomerID] IS NOT NULL +) AS [t]"); + } + + public override async Task SelectMany_with_collection_being_correlated_subquery_which_references_non_mapped_properties_from_inner_and_outer_entity(bool isAsync) + { + await base.SelectMany_with_collection_being_correlated_subquery_which_references_non_mapped_properties_from_inner_and_outer_entity(isAsync); + + AssertSql( + @""); + } + + public override async Task Select_with_complex_expression_that_can_be_funcletized(bool isAsync) + { + await base.Select_with_complex_expression_that_can_be_funcletized(isAsync); + + AssertSql( + @"SELECT CASE + WHEN N'' = N'' THEN 0 + ELSE CHARINDEX(N'', [c].[ContactName]) - 1 +END +FROM [Customers] AS [c] +WHERE [c].[CustomerID] = N'ALFKI'"); + } + + public override async Task Select_chained_entity_navigation_doesnt_materialize_intermittent_entities(bool isAsync) + { + await base.Select_chained_entity_navigation_doesnt_materialize_intermittent_entities(isAsync); + + AssertSql( + @"SELECT [o].[OrderID], [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +LEFT JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID] +ORDER BY [o].[OrderID], [o0].[OrderID]"); + } + + public override async Task Select_entity_compared_to_null(bool isAsync) + { + await base.Select_entity_compared_to_null(isAsync); + + AssertSql( + @"SELECT CASE + WHEN [c].[CustomerID] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [Orders] AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE ([o].[CustomerID] = N'ALFKI') AND [o].[CustomerID] IS NOT NULL"); + } } }