diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index 956a0b8089d..7cea7eb37d4 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -58,6 +58,9 @@ protected EntityReference UnwrapEntityReference(Expression expression) case OwnedNavigationReference ownedNavigationReference: return ownedNavigationReference.EntityReference; + case NullConditionalExpression nullConditionalExpression: + return UnwrapEntityReference(nullConditionalExpression.AccessOperation); + default: return null; } @@ -66,8 +69,8 @@ protected EntityReference UnwrapEntityReference(Expression expression) protected override Expression VisitMember(MemberExpression memberExpression) { var innerExpression = Visit(memberExpression.Expression); - var expansion = TryExpandNavigation(innerExpression, MemberIdentity.Create(memberExpression.Member)); - return expansion ?? memberExpression.Update(innerExpression); + return TryExpandNavigation(innerExpression, MemberIdentity.Create(memberExpression.Member)) + ?? memberExpression.Update(innerExpression); } protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) @@ -75,11 +78,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var navigationName)) { source = Visit(source); - var expansion = TryExpandNavigation(source, MemberIdentity.Create(navigationName)); - if (expansion != null) - { - return expansion; - } + return TryExpandNavigation(source, MemberIdentity.Create(navigationName)) + ?? methodCallExpression.Update(null, new[] { source, methodCallExpression.Arguments[1] }); } return base.VisitMethodCall(methodCallExpression); @@ -602,5 +602,20 @@ public override Expression Visit(Expression expression) } } } + + private class EntityReferenceOptionalMarkingExpressionVisitor : ExpressionVisitor + { + public override Expression Visit(Expression expression) + { + if (expression is EntityReference entityReference) + { + entityReference.MarkAsOptional(); + + return entityReference; + } + + return base.Visit(expression); + } + } } } diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 5591ee354b4..8c3a7434b64 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -27,6 +27,7 @@ public partial class NavigationExpandingExpressionVisitor : ExpressionVisitor private readonly PendingSelectorExpandingExpressionVisitor _pendingSelectorExpandingExpressionVisitor; private readonly SubqueryMemberPushdownExpressionVisitor _subqueryMemberPushdownExpressionVisitor; private readonly ReducingExpressionVisitor _reducingExpressionVisitor; + private readonly EntityReferenceOptionalMarkingExpressionVisitor _entityReferenceOptionalMarkingExpressionVisitor; private readonly ISet _parameterNames = new HashSet(); private static readonly MethodInfo _enumerableToListMethodInfo = typeof(Enumerable).GetTypeInfo() .GetDeclaredMethods(nameof(Enumerable.ToList)) @@ -39,6 +40,7 @@ public NavigationExpandingExpressionVisitor(QueryCompilationContext queryCompila _pendingSelectorExpandingExpressionVisitor = new PendingSelectorExpandingExpressionVisitor(this); _subqueryMemberPushdownExpressionVisitor = new SubqueryMemberPushdownExpressionVisitor(); _reducingExpressionVisitor = new ReducingExpressionVisitor(); + _entityReferenceOptionalMarkingExpressionVisitor = new EntityReferenceOptionalMarkingExpressionVisitor(); } private string GetParameterName(string prefix) @@ -898,12 +900,15 @@ private Expression ProcessLeftJoin( Expression.Quote(innerKeySelector), Expression.Quote(newResultSelector)); + var innerPendingSelector = innerSource.PendingSelector; + innerPendingSelector = _entityReferenceOptionalMarkingExpressionVisitor.Visit(innerPendingSelector); + var currentTree = new NavigationTreeNode(outerSource.CurrentTree, innerSource.CurrentTree); var pendingSelector = new ReplacingExpressionVisitor( new Dictionary { { resultSelector.Parameters[0], outerSource.PendingSelector }, - { resultSelector.Parameters[1], innerSource.PendingSelector } + { resultSelector.Parameters[1], innerPendingSelector } }).Visit(resultSelector.Body); var parameterName = GetParameterName("ti"); diff --git a/src/EFCore/Query/Internal/NullCheckRemovingExpressionVisitor.cs b/src/EFCore/Query/Internal/NullCheckRemovingExpressionVisitor.cs index 02b35326e4d..899c921a145 100644 --- a/src/EFCore/Query/Internal/NullCheckRemovingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NullCheckRemovingExpressionVisitor.cs @@ -73,10 +73,6 @@ private class NullSafeAccessVerifyingExpressionVisitor : ExpressionVisitor { private readonly ISet _nullSafeAccesses = new HashSet(ExpressionEqualityComparer.Instance); - public NullSafeAccessVerifyingExpressionVisitor() - { - } - public virtual bool Verify(Expression caller, Expression result) { _nullSafeAccesses.Clear(); @@ -87,19 +83,9 @@ public virtual bool Verify(Expression caller, Expression result) } public override Expression Visit(Expression expression) - { - if (expression == null) - { - return expression; - } - - if (_nullSafeAccesses.Contains(expression)) - { - return expression; - } - - return base.Visit(expression); - } + => expression == null || _nullSafeAccesses.Contains(expression) + ? expression + : base.Visit(expression); protected override Expression VisitMember(MemberExpression memberExpression) { @@ -127,14 +113,7 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) } private bool IsNullConstant(Expression expression) - { - if (expression is ConstantExpression constantExpression - && constantExpression.Value == null) - { - return true; - } - - return false; - } + => expression is ConstantExpression constantExpression + && constantExpression.Value == null; } } diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs index 8356ef3b08a..28f2398d321 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs @@ -170,9 +170,9 @@ public virtual Task Key_equality_two_conditions_on_same_navigation(bool isAsync) isAsync, l1s => l1s.Where( l => l.OneToOne_Required_FK1 == new Level2 - { - Id = 1 - } + { + Id = 1 + } || l.OneToOne_Required_FK1 == new Level2 { Id = 2 @@ -192,9 +192,9 @@ public virtual Task Key_equality_two_conditions_on_same_navigation2(bool isAsync isAsync, l2s => l2s.Where( l => l.OneToOne_Required_FK_Inverse2 == new Level1 - { - Id = 1 - } + { + Id = 1 + } || l.OneToOne_Required_FK_Inverse2 == new Level1 { Id = 2 @@ -3365,7 +3365,8 @@ from l3 in grouping.DefaultIfEmpty() orderby l2.OneToOne_Optional_FK2.Id select new { - Entity = l4, Collection = l2.OneToMany_Optional_Self2.Where(e => e.Id != 42).ToList(), + Entity = l4, + Collection = l2.OneToMany_Optional_Self2.Where(e => e.Id != 42).ToList(), Property = l3.OneToOne_Optional_FK_Inverse3.OneToOne_Required_FK2.Name }, (l1s, l4s) @@ -3578,7 +3579,7 @@ public virtual Task Required_navigation_on_a_subquery_with_First_in_predicate(bo .Where(l1 => l2s.OrderBy(l2i => l2i.Id).First().OneToOne_Required_FK_Inverse2.Name == "L1 02")); } - [ConditionalTheory(Skip = "Issue#16088")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Manually_created_left_join_propagates_nullability_to_navigations(bool isAsync) { @@ -6227,22 +6228,20 @@ public virtual Task Include_multiple_collections_on_same_level(bool isAsync) assertOrder: true); } - [ConditionalTheory(Skip = "Issue#16088")] + [ConditionalTheory(Skip = "Issue#17020")] [MemberData(nameof(IsAsyncData))] public virtual Task Null_check_removal_applied_recursively(bool isAsync) { return AssertQuery( isAsync, l1s => l1s.Where(l1 => - ((((l1.OneToOne_Optional_FK1 == null + (((l1.OneToOne_Optional_FK1 == null ? null : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2) == null ? null : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3) == null ? null - : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3) == null - ? null - : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3.Name) == "L4 01")); + : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3.Name) == "L4 01")); } [ConditionalTheory] @@ -6258,9 +6257,7 @@ public virtual Task Null_check_different_structure_does_not_remove_null_checks(b ? null : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3 == null ? null - : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3 == null - ? null - : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3.Name) == "L4 01")); + : l1.OneToOne_Optional_FK1.OneToOne_Optional_FK2.OneToOne_Optional_FK3.Name) == "L4 01")); } } } diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index 8e41400d1cd..af5bfa4b2a3 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -5104,7 +5104,7 @@ public virtual Task Correlated_collections_with_FirstOrDefault(bool isAsync) elementAsserter: (e, a) => CollectionAsserter(elementAsserter: (ee, aa) => Assert.Equal(ee.Nickname, aa.Nickname))); } - [ConditionalTheory(Skip = "Issue#16088")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Correlated_collections_on_left_join_with_predicate(bool isAsync) { @@ -5124,7 +5124,7 @@ from g in grouping.DefaultIfEmpty() from t in ts join g in gs on t.GearNickName equals g.Nickname into grouping from g in grouping.DefaultIfEmpty() - where !MaybeScalar(g, () => g.HasSoulPatch) == true || g == null + where !MaybeScalar(g, () => g.HasSoulPatch) == true select new { Nickname = Maybe(g, () => g.Nickname), @@ -5138,7 +5138,7 @@ from g in grouping.DefaultIfEmpty() }); } - [ConditionalTheory(Skip = "Issue#16088")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Correlated_collections_on_left_join_with_null_value(bool isAsync) { @@ -5160,7 +5160,7 @@ orderby t.Note elementAsserter: (e, a) => CollectionAsserter(ee => ee)); } - [ConditionalTheory(Skip = "Issue#16088")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Correlated_collections_left_join_with_self_reference(bool isAsync) { @@ -5192,7 +5192,7 @@ from o in grouping.DefaultIfEmpty() }); } - [ConditionalTheory(Skip = "Issue#16088")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Correlated_collections_deeply_nested_left_join(bool isAsync) { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index bb80f6f9ece..8ae0939a573 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -5150,29 +5150,16 @@ public override async Task Correlated_collections_on_left_join_with_predicate(bo await base.Correlated_collections_on_left_join_with_predicate(isAsync); AssertSql( - @"SELECT [t0].[Nickname], [t0].[FullName] -FROM [Tags] AS [t] + @"SELECT [t].[Nickname], [t0].[Id], [w].[Name], [w].[Id], [w].[OwnerFullName] +FROM [Tags] AS [t0] LEFT JOIN ( - SELECT [g].* + 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] - WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [t0] ON [t].[GearNickName] = [t0].[Nickname] -WHERE ([t0].[HasSoulPatch] <> CAST(1 AS bit)) OR [t0].[HasSoulPatch] IS NULL -ORDER BY [t0].[Nickname], [t0].[SquadId], [t0].[FullName]", - // - @"SELECT [t3].[Nickname], [t3].[SquadId], [t3].[FullName], [g.Weapons].[Name], [g.Weapons].[OwnerFullName] -FROM [Weapons] AS [g.Weapons] -INNER JOIN ( - SELECT [t2].[Nickname], [t2].[SquadId], [t2].[FullName] - FROM [Tags] AS [t1] - LEFT JOIN ( - SELECT [g0].* - FROM [Gears] AS [g0] - WHERE [g0].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t2] ON [t1].[GearNickName] = [t2].[Nickname] - WHERE ([t2].[HasSoulPatch] <> CAST(1 AS bit)) OR [t2].[HasSoulPatch] IS NULL -) AS [t3] ON [g.Weapons].[OwnerFullName] = [t3].[FullName] -ORDER BY [t3].[Nickname], [t3].[SquadId], [t3].[FullName]"); + WHERE [g].[Discriminator] IN (N'Gear', N'Officer') +) AS [t] ON [t0].[GearNickName] = [t].[Nickname] +LEFT JOIN [Weapons] AS [w] ON [t].[FullName] = [w].[OwnerFullName] +WHERE [t].[HasSoulPatch] <> CAST(1 AS bit) +ORDER BY [t0].[Id], [w].[Id]"); } public override async Task Correlated_collections_on_left_join_with_null_value(bool isAsync) @@ -5180,27 +5167,15 @@ public override async Task Correlated_collections_on_left_join_with_null_value(b await base.Correlated_collections_on_left_join_with_null_value(isAsync); AssertSql( - @"SELECT [t0].[FullName] + @"SELECT [t].[Id], [w].[Name], [w].[Id], [w].[OwnerFullName] FROM [Tags] AS [t] LEFT JOIN ( - SELECT [g].* + 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] - WHERE [g].[Discriminator] IN (N'Officer', N'Gear') + WHERE [g].[Discriminator] IN (N'Gear', N'Officer') ) AS [t0] ON [t].[GearNickName] = [t0].[Nickname] -ORDER BY [t].[Note], [t0].[Nickname], [t0].[SquadId], [t0].[FullName]", - // - @"SELECT [t3].[Note], [t3].[Nickname], [t3].[SquadId], [t3].[FullName], [g.Weapons].[Name], [g.Weapons].[OwnerFullName] -FROM [Weapons] AS [g.Weapons] -INNER JOIN ( - SELECT [t1].[Note], [t2].[Nickname], [t2].[SquadId], [t2].[FullName] - FROM [Tags] AS [t1] - LEFT JOIN ( - SELECT [g0].* - FROM [Gears] AS [g0] - WHERE [g0].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t2] ON [t1].[GearNickName] = [t2].[Nickname] -) AS [t3] ON [g.Weapons].[OwnerFullName] = [t3].[FullName] -ORDER BY [t3].[Note], [t3].[Nickname], [t3].[SquadId], [t3].[FullName]"); +LEFT JOIN [Weapons] AS [w] ON [t0].[FullName] = [w].[OwnerFullName] +ORDER BY [t].[Note], [t].[Id], [w].[Id]"); } public override async Task Correlated_collections_left_join_with_self_reference(bool isAsync) @@ -5208,28 +5183,19 @@ public override async Task Correlated_collections_left_join_with_self_reference( await base.Correlated_collections_left_join_with_self_reference(isAsync); AssertSql( - @"SELECT [t].[Note], [t0].[Nickname], [t0].[SquadId] + @"SELECT [t].[Note], [t].[Id], [t0].[FullName], [t0].[Nickname], [t0].[SquadId], [t0].[LeaderNickname], [t0].[LeaderSquadId] FROM [Tags] AS [t] LEFT JOIN ( - SELECT [o].* - FROM [Gears] AS [o] - WHERE [o].[Discriminator] = N'Officer' -) AS [t0] ON [t].[GearNickName] = [t0].[Nickname] -ORDER BY [t0].[Nickname], [t0].[SquadId]", - // - @"SELECT [t3].[Nickname], [t3].[SquadId], [o.Reports].[FullName], [o.Reports].[LeaderNickname], [o.Reports].[LeaderSquadId] -FROM [Gears] AS [o.Reports] -INNER JOIN ( - SELECT [t2].[Nickname], [t2].[SquadId] - FROM [Tags] AS [t1] - LEFT JOIN ( - SELECT [o0].* - FROM [Gears] AS [o0] - WHERE [o0].[Discriminator] = N'Officer' - ) AS [t2] ON [t1].[GearNickName] = [t2].[Nickname] -) AS [t3] ON ([o.Reports].[LeaderNickname] = [t3].[Nickname]) AND ([o.Reports].[LeaderSquadId] = [t3].[SquadId]) -WHERE [o.Reports].[Discriminator] IN (N'Officer', N'Gear') -ORDER BY [t3].[Nickname], [t3].[SquadId]"); + 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') AND ([g0].[Discriminator] = N'Officer') +) AS [t1] ON [t].[GearNickName] = [t1].[Nickname] +LEFT JOIN ( + SELECT [g].[FullName], [g].[Nickname], [g].[SquadId], [g].[LeaderNickname], [g].[LeaderSquadId] + FROM [Gears] AS [g] + WHERE [g].[Discriminator] IN (N'Gear', N'Officer') +) AS [t0] ON ((([t1].[Nickname] = [t0].[LeaderNickname]) AND ([t1].[Nickname] IS NOT NULL AND [t0].[LeaderNickname] IS NOT NULL)) OR ([t1].[Nickname] IS NULL AND [t0].[LeaderNickname] IS NULL)) AND (([t1].[SquadId] = [t0].[LeaderSquadId]) AND [t1].[SquadId] IS NOT NULL) +ORDER BY [t].[Id], [t0].[Nickname], [t0].[SquadId]"); } public override async Task Correlated_collections_deeply_nested_left_join(bool isAsync) @@ -5237,50 +5203,25 @@ public override async Task Correlated_collections_deeply_nested_left_join(bool i await base.Correlated_collections_deeply_nested_left_join(isAsync); AssertSql( - @"SELECT [g.Squad].[Id] + @"SELECT [t].[Id], [t1].[Nickname], [t1].[SquadId], [t1].[Id], [t1].[AmmunitionType], [t1].[IsAutomatic], [t1].[Name], [t1].[OwnerFullName], [t1].[SynergyWithId] FROM [Tags] AS [t] LEFT JOIN ( - SELECT [g].* + 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 [t2] ON [t].[GearNickName] = [t2].[Nickname] +LEFT JOIN [Squads] AS [s] ON [t2].[SquadId] = [s].[Id] +LEFT JOIN ( + SELECT [g].[Nickname], [g].[SquadId], [t0].[Id], [t0].[AmmunitionType], [t0].[IsAutomatic], [t0].[Name], [t0].[OwnerFullName], [t0].[SynergyWithId] FROM [Gears] AS [g] - WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [t0] ON [t].[GearNickName] = [t0].[Nickname] -LEFT JOIN [Squads] AS [g.Squad] ON [t0].[SquadId] = [g.Squad].[Id] -ORDER BY [t].[Note], [t0].[Nickname] DESC, [t0].[SquadId], [g.Squad].[Id]", - // - @"SELECT [t3].[Note], [t3].[Nickname], [t3].[SquadId], [t3].[Id], [g.Squad.Members].[Nickname] AS [Nickname0], [g.Squad.Members].[SquadId], [g.Squad.Members].[FullName] -FROM [Gears] AS [g.Squad.Members] -INNER JOIN ( - SELECT [t1].[Note], [t2].[Nickname], [t2].[SquadId], [g.Squad0].[Id] - FROM [Tags] AS [t1] LEFT JOIN ( - SELECT [g0].* - FROM [Gears] AS [g0] - WHERE [g0].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t2] ON [t1].[GearNickName] = [t2].[Nickname] - LEFT JOIN [Squads] AS [g.Squad0] ON [t2].[SquadId] = [g.Squad0].[Id] -) AS [t3] ON [g.Squad.Members].[SquadId] = [t3].[Id] -WHERE [g.Squad.Members].[Discriminator] IN (N'Officer', N'Gear') AND ([g.Squad.Members].[HasSoulPatch] = CAST(1 AS bit)) -ORDER BY [t3].[Note], [t3].[Nickname] DESC, [t3].[SquadId], [t3].[Id], [g.Squad.Members].[Nickname], [g.Squad.Members].[SquadId], [g.Squad.Members].[FullName]", - // - @"SELECT [g.Squad.Members.Weapons].[Id], [g.Squad.Members.Weapons].[AmmunitionType], [g.Squad.Members.Weapons].[IsAutomatic], [g.Squad.Members.Weapons].[Name], [g.Squad.Members.Weapons].[OwnerFullName], [g.Squad.Members.Weapons].[SynergyWithId], [t7].[Note], [t7].[Nickname], [t7].[SquadId], [t7].[Id], [t7].[Nickname0], [t7].[SquadId0], [t7].[FullName] -FROM [Weapons] AS [g.Squad.Members.Weapons] -INNER JOIN ( - SELECT [t6].[Note], [t6].[Nickname], [t6].[SquadId], [t6].[Id], [g.Squad.Members0].[Nickname] AS [Nickname0], [g.Squad.Members0].[SquadId] AS [SquadId0], [g.Squad.Members0].[FullName] - FROM [Gears] AS [g.Squad.Members0] - INNER JOIN ( - SELECT [t4].[Note], [t5].[Nickname], [t5].[SquadId], [g.Squad1].[Id] - FROM [Tags] AS [t4] - LEFT JOIN ( - SELECT [g1].* - FROM [Gears] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t5] ON [t4].[GearNickName] = [t5].[Nickname] - LEFT JOIN [Squads] AS [g.Squad1] ON [t5].[SquadId] = [g.Squad1].[Id] - ) AS [t6] ON [g.Squad.Members0].[SquadId] = [t6].[Id] - WHERE [g.Squad.Members0].[Discriminator] IN (N'Officer', N'Gear') AND ([g.Squad.Members0].[HasSoulPatch] = CAST(1 AS bit)) -) AS [t7] ON [g.Squad.Members.Weapons].[OwnerFullName] = [t7].[FullName] -WHERE [g.Squad.Members.Weapons].[IsAutomatic] = CAST(1 AS bit) -ORDER BY [t7].[Note], [t7].[Nickname] DESC, [t7].[SquadId], [t7].[Id], [t7].[Nickname0], [t7].[SquadId0], [t7].[FullName]"); + SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] + FROM [Weapons] AS [w] + WHERE [w].[IsAutomatic] = CAST(1 AS bit) + ) AS [t0] ON [g].[FullName] = [t0].[OwnerFullName] + WHERE [g].[Discriminator] IN (N'Gear', N'Officer') AND ([g].[HasSoulPatch] = CAST(1 AS bit)) +) AS [t1] ON [s].[Id] = [t1].[SquadId] +ORDER BY [t].[Note], [t2].[Nickname] DESC, [t].[Id], [t1].[Nickname], [t1].[SquadId], [t1].[Id]"); } public override async Task Correlated_collections_from_left_join_with_additional_elements_projected_of_that_join(bool isAsync)