diff --git a/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs index 18507b1f537..4051fd25384 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.InMemory.Internal; using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel; @@ -70,5 +68,11 @@ public override async Task Projecting_some_properties_as_well_as_correlated_coll Assert.Equal(InMemoryStrings.DistinctOnSubqueryNotSupported, message); } + + [ConditionalTheory(Skip = "Issue #25735")] + public override Task Project_navigation_defined_on_derived_from_entity_with_inheritance_using_soft_cast(bool async) + { + return base.Project_navigation_defined_on_derived_from_entity_with_inheritance_using_soft_cast(async); + } } } diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryFixtureBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryFixtureBase.cs index 5b8e0975af6..63e62fed159 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryFixtureBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryFixtureBase.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel; using Microsoft.EntityFrameworkCore.TestUtilities; @@ -293,6 +290,22 @@ public IReadOnlyDictionary GetEntityAsserters() Assert.Equal(ee.SynergyWithId, aa.SynergyWithId); } } + }, + { + typeof(LocustHighCommand), (e, a) => + { + Assert.Equal(e == null, a == null); + + if (a != null) + { + var ee = (LocustHighCommand)e; + var aa = (LocustHighCommand)a; + + Assert.Equal(ee.Id, aa.Id); + Assert.Equal(ee.IsOperational, aa.IsOperational); + Assert.Equal(ee.Name, aa.Name); + } + } } }.ToDictionary(e => e.Key, e => (object)e.Value); diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index 3036e064e4c..65a32ded194 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -1,12 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Net; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Diagnostics.Internal; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -8978,6 +8974,146 @@ public virtual Task Contains_on_readonly_enumerable(bool async) ss => ss.Set().Where(w => _weaponTypes.Contains(w.AmmunitionType))); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_navigation_defined_on_base_from_entity_with_inheritance_using_soft_cast(bool async) + { + return AssertQuery( + async, + ss => ss.Set().Select(g => new + { + Gear = g, + Tag = (g as Officer).Tag, + IsNull = (g as Officer).Tag == null, + Property = (g as Officer).Nickname, + PropertyAfterNavigation = (g as Officer).Tag.Id, + NestedOuter = new + { + CityOfBirth = (g as Officer).CityOfBirth, + IsNull = (g as Officer).CityOfBirth == null, + Property = (g as Officer).Nickname, + PropertyAfterNavigation = (g as Officer).CityOfBirth.Name, + NestedInner = new + { + Squad = (g as Officer).Squad, + IsNull = (g as Officer).Squad == null, + Property = (g as Officer).Nickname, + PropertyAfterNavigation = (g as Officer).Squad.Id + } + } + }), + ss => ss.Set().Select(g => new + { + Gear = g, + Tag = g.Tag, + IsNull = g.Tag == null, + Property = g.Nickname, + PropertyAfterNavigation = g.Tag.Id, + NestedOuter = new + { + CityOfBirth = g.CityOfBirth, + IsNull = g.CityOfBirth == null, + Property = g.Nickname, + PropertyAfterNavigation = g.CityOfBirth.Name, + NestedInner = new + { + Squad = g.Squad, + IsNull = g.Squad == null, + Property = g.Nickname, + PropertyAfterNavigation = g.Squad.Id + } + } + }), + elementSorter: e => e.Gear.Nickname, + elementAsserter: (e, a) => + { + AssertEqual(e.Gear, a.Gear); + AssertEqual(e.Tag, a.Tag); + AssertEqual(e.IsNull, a.IsNull); + AssertEqual(e.Property, a.Property); + AssertEqual(e.PropertyAfterNavigation, a.PropertyAfterNavigation); + + AssertEqual(e.NestedOuter.CityOfBirth, a.NestedOuter.CityOfBirth); + AssertEqual(e.NestedOuter.IsNull, a.NestedOuter.IsNull); + AssertEqual(e.NestedOuter.Property, a.NestedOuter.Property); + AssertEqual(e.NestedOuter.PropertyAfterNavigation, a.NestedOuter.PropertyAfterNavigation); + + AssertEqual(e.NestedOuter.NestedInner.Squad, a.NestedOuter.NestedInner.Squad); + AssertEqual(e.NestedOuter.NestedInner.IsNull, a.NestedOuter.NestedInner.IsNull); + AssertEqual(e.NestedOuter.NestedInner.Property, a.NestedOuter.NestedInner.Property); + AssertEqual(e.NestedOuter.NestedInner.PropertyAfterNavigation, a.NestedOuter.NestedInner.PropertyAfterNavigation); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_navigation_defined_on_derived_from_entity_with_inheritance_using_soft_cast(bool async) + { + return AssertQuery( + async, + ss => ss.Set().Select(l => new + { + Leader = l, + DefeatedBy = (l as LocustCommander).DefeatedBy, + IsNull = (l as LocustCommander).DefeatedBy == null, + Property = (l as LocustCommander).DefeatedByNickname, + PropertyAfterNavigation = (bool?)(l as LocustCommander).DefeatedBy.HasSoulPatch, + NestedOuter = new + { + CommandingFaction = (l as LocustCommander).CommandingFaction, + IsNull = (l as LocustCommander).CommandingFaction == null, + Property = (int?)(l as LocustCommander).HighCommandId, + PropertyAfterNavigation = (l as LocustCommander).CommandingFaction.Eradicated, + NestedInner = new + { + HighCommand = (l as LocustCommander).HighCommand, + IsNull = (l as LocustCommander).HighCommand == null, + Property = (l as LocustCommander).DefeatedBySquadId, + PropertyAfterNavigation = (l as LocustCommander).HighCommand.Name + } + } + }), + ss => ss.Set().Select(l => new + { + Leader = l, + DefeatedBy = (l as LocustCommander).DefeatedBy, + IsNull = (l as LocustCommander).DefeatedBy == null, + Property = (l as LocustCommander).DefeatedByNickname, + PropertyAfterNavigation = (bool?)(l as LocustCommander).DefeatedBy.HasSoulPatch, + NestedOuter = new + { + CommandingFaction = (l as LocustCommander).CommandingFaction, + IsNull = (l as LocustCommander).CommandingFaction == null, + Property = (int?)(l as LocustCommander).HighCommandId, + PropertyAfterNavigation = (l as LocustCommander).CommandingFaction.MaybeScalar(x => x.Eradicated), + NestedInner = new + { + HighCommand = (l as LocustCommander).HighCommand, + IsNull = (l as LocustCommander).HighCommand == null, + Property = (l as LocustCommander).MaybeScalar(x => x.DefeatedBySquadId), + PropertyAfterNavigation = (l as LocustCommander).HighCommand.Name + } + } + }), + elementSorter: e => e.Leader.Name, + elementAsserter: (e, a) => + { + AssertEqual(e.Leader, a.Leader); + AssertEqual(e.DefeatedBy, a.DefeatedBy); + AssertEqual(e.IsNull, a.IsNull); + AssertEqual(e.Property, a.Property); + AssertEqual(e.PropertyAfterNavigation, a.PropertyAfterNavigation); + AssertEqual(e.NestedOuter.CommandingFaction, a.NestedOuter.CommandingFaction); + AssertEqual(e.NestedOuter.IsNull, a.NestedOuter.IsNull); + AssertEqual(e.NestedOuter.Property, a.NestedOuter.Property); + AssertEqual(e.NestedOuter.PropertyAfterNavigation, a.NestedOuter.PropertyAfterNavigation); + AssertEqual(e.NestedOuter.NestedInner.HighCommand, a.NestedOuter.NestedInner.HighCommand); + AssertEqual(e.NestedOuter.NestedInner.IsNull, a.NestedOuter.NestedInner.IsNull); + AssertEqual(e.NestedOuter.NestedInner.Property, a.NestedOuter.NestedInner.Property); + AssertEqual(e.NestedOuter.NestedInner.PropertyAfterNavigation, a.NestedOuter.NestedInner.PropertyAfterNavigation); + }); + } + protected GearsOfWarContext CreateContext() => Fixture.CreateContext(); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs index 9451a5b9d90..bb7bbfe7884 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.TestModels.Northwind; using Microsoft.EntityFrameworkCore.TestUtilities; using Xunit; @@ -2038,7 +2034,7 @@ public virtual Task Projecting_count_of_navigation_which_is_generic_collection(b assertOrder: true); } - [ConditionalTheory(Skip = "issue #22701")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Projecting_count_of_navigation_which_is_generic_collection_using_convert(bool async) { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index d3ad78345d1..651fa6a91ba 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Linq; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel; using Microsoft.EntityFrameworkCore.TestUtilities; using Xunit; @@ -8157,6 +8155,48 @@ FROM [Weapons] AS [w] WHERE [w].[AmmunitionType] = 1"); } + public override async Task Project_navigation_defined_on_base_from_entity_with_inheritance_using_soft_cast(bool async) + { + await base.Project_navigation_defined_on_base_from_entity_with_inheritance_using_soft_cast(async); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note], CASE + WHEN [t].[Id] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull], [c].[Name], [c].[Location], [c].[Nation], CASE + WHEN [c].[Name] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull], [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], CASE + WHEN [s].[Id] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull] +FROM [Gears] AS [g] +LEFT JOIN [Tags] AS [t] ON ([g].[Nickname] = [t].[GearNickName]) AND ([g].[SquadId] = [t].[GearSquadId]) +LEFT JOIN [Cities] AS [c] ON [g].[CityOfBirthName] = [c].[Name] +LEFT JOIN [Squads] AS [s] ON [g].[SquadId] = [s].[Id]"); + } + + public override async Task Project_navigation_defined_on_derived_from_entity_with_inheritance_using_soft_cast(bool async) + { + await base.Project_navigation_defined_on_derived_from_entity_with_inheritance_using_soft_cast(async); + + AssertSql( + @"SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[ThreatLevelByte], [l].[ThreatLevelNullableByte], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId], [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE + WHEN [g].[Nickname] IS NULL OR [g].[SquadId] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull], [f].[Id], [f].[CapitalName], [f].[Discriminator], [f].[Name], [f].[ServerAddress], [f].[CommanderName], [f].[Eradicated], CASE + WHEN [f].[Id] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull], [l0].[Id], [l0].[IsOperational], [l0].[Name], CASE + WHEN [l0].[Id] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull] +FROM [LocustLeaders] AS [l] +LEFT JOIN [Gears] AS [g] ON ([l].[DefeatedByNickname] = [g].[Nickname]) AND ([l].[DefeatedBySquadId] = [g].[SquadId]) +LEFT JOIN [Factions] AS [f] ON [l].[Name] = [f].[CommanderName] +LEFT JOIN [LocustHighCommands] AS [l0] ON [l].[HighCommandId] = [l0].[Id]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs index 809b4b1b204..f31f36a88cb 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.TestUtilities; -using Xunit; using Xunit.Abstractions; namespace Microsoft.EntityFrameworkCore.Query @@ -1476,6 +1472,17 @@ FROM [Customers] AS [c] ORDER BY [c].[CustomerID]"); } + public override async Task Projecting_count_of_navigation_which_is_generic_collection_using_convert(bool async) + { + await base.Projecting_count_of_navigation_which_is_generic_collection_using_convert(async); + + AssertSql( + @"SELECT [c].[CustomerID], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +ORDER BY [c].[CustomerID]"); + } + public override async Task Projection_take_projection_doesnt_project_intermittent_column(bool async) { await base.Projection_take_projection_doesnt_project_intermittent_column(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index 223829e62b3..bf9c34031ec 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Linq; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel; using Xunit; using Xunit.Abstractions; @@ -9087,6 +9085,64 @@ public override async Task Where_TimeOnly_subtract_TimeOnly(bool async) AssertSql(""); } + public override async Task Project_navigation_defined_on_base_from_entity_with_inheritance_using_soft_cast(bool async) + { + await base.Project_navigation_defined_on_base_from_entity_with_inheritance_using_soft_cast(async); + + AssertSql( + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE + WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' +END AS [Discriminator], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note], CASE + WHEN [t].[Id] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull], [c].[Name], [c].[Location], [c].[Nation], CASE + WHEN [c].[Name] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull], [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], CASE + WHEN [s].[Id] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull] +FROM [Gears] AS [g] +LEFT JOIN [Officers] AS [o] ON ([g].[Nickname] = [o].[Nickname]) AND ([g].[SquadId] = [o].[SquadId]) +LEFT JOIN [Tags] AS [t] ON ([g].[Nickname] = [t].[GearNickName]) AND ([g].[SquadId] = [t].[GearSquadId]) +LEFT JOIN [Cities] AS [c] ON [g].[CityOfBirthName] = [c].[Name] +LEFT JOIN [Squads] AS [s] ON [g].[SquadId] = [s].[Id]"); + } + + public override async Task Project_navigation_defined_on_derived_from_entity_with_inheritance_using_soft_cast(bool async) + { + await base.Project_navigation_defined_on_derived_from_entity_with_inheritance_using_soft_cast(async); + + AssertSql( + @"SELECT [l].[Name], [l].[LocustHordeId], [l].[ThreatLevel], [l].[ThreatLevelByte], [l].[ThreatLevelNullableByte], [l0].[DefeatedByNickname], [l0].[DefeatedBySquadId], [l0].[HighCommandId], CASE + WHEN [l0].[Name] IS NOT NULL THEN N'LocustCommander' +END AS [Discriminator], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator], CASE + WHEN [t].[Nickname] IS NULL OR [t].[SquadId] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull], [t0].[Id], [t0].[CapitalName], [t0].[Name], [t0].[ServerAddress], [t0].[CommanderName], [t0].[Eradicated], CASE + WHEN [t0].[Id] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull], [l2].[Id], [l2].[IsOperational], [l2].[Name], CASE + WHEN [l2].[Id] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNull] +FROM [LocustLeaders] AS [l] +LEFT JOIN [LocustCommanders] AS [l0] ON [l].[Name] = [l0].[Name] +LEFT JOIN ( + SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE + WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' + END AS [Discriminator] + FROM [Gears] AS [g] + LEFT JOIN [Officers] AS [o] ON ([g].[Nickname] = [o].[Nickname]) AND ([g].[SquadId] = [o].[SquadId]) +) AS [t] ON ([l0].[DefeatedByNickname] = [t].[Nickname]) AND ([l0].[DefeatedBySquadId] = [t].[SquadId]) +LEFT JOIN ( + SELECT [f].[Id], [f].[CapitalName], [f].[Name], [f].[ServerAddress], [l1].[CommanderName], [l1].[Eradicated] + FROM [Factions] AS [f] + INNER JOIN [LocustHordes] AS [l1] ON [f].[Id] = [l1].[Id] +) AS [t0] ON [l].[Name] = [t0].[CommanderName] +LEFT JOIN [LocustHighCommands] AS [l2] ON [l0].[HighCommandId] = [l2].[Id]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); }