Skip to content

Commit ab4c1eb

Browse files
authored
Query: Handle multiple lets with weak entities (#22259)
Resolves #13303 Normal scenario worked already. Weak entities needed some additional handling
1 parent ff27e02 commit ab4c1eb

File tree

7 files changed

+149
-40
lines changed

7 files changed

+149
-40
lines changed

src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
13371337
Check.NotNull(extensionExpression, nameof(extensionExpression));
13381338

13391339
return extensionExpression is EntityShaperExpression
1340+
|| extensionExpression is ShapedQueryExpression
13401341
? extensionExpression
13411342
: base.VisitExtension(extensionExpression);
13421343
}

src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
12801280
Check.NotNull(extensionExpression, nameof(extensionExpression));
12811281

12821282
return extensionExpression is EntityShaperExpression
1283+
|| extensionExpression is ShapedQueryExpression
12831284
? extensionExpression
12841285
: base.VisitExtension(extensionExpression);
12851286
}

src/EFCore/Query/ReplacingExpressionVisitor.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ public ReplacingExpressionVisitor([NotNull] IReadOnlyList<Expression> originals,
5858
/// <inheritdoc />
5959
public override Expression Visit(Expression expression)
6060
{
61-
if (expression == null)
61+
if (expression == null
62+
|| expression is ShapedQueryExpression
63+
|| expression is EntityShaperExpression)
6264
{
6365
return expression;
6466
}

test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs

+23-1
Original file line numberDiff line numberDiff line change
@@ -5538,7 +5538,8 @@ public virtual Task Member_over_null_check_ternary_and_nested_dto_type(bool asyn
55385538
? null
55395539
: new Level2Dto
55405540
{
5541-
Id = l1.OneToOne_Optional_FK1.Id, Name = l1.OneToOne_Optional_FK1.Name,
5541+
Id = l1.OneToOne_Optional_FK1.Id,
5542+
Name = l1.OneToOne_Optional_FK1.Name,
55425543
}
55435544
})
55445545
.OrderBy(e => e.Level2.Name)
@@ -5645,5 +5646,26 @@ where l1.Id < 3
56455646
orderby l3.Id
56465647
select l3).Distinct().Take(1).OrderBy(e => e.Id).FirstOrDefault().Name);
56475648
}
5649+
5650+
[ConditionalTheory]
5651+
[MemberData(nameof(IsAsyncData))]
5652+
public virtual Task Let_let_contains_from_outer_let(bool async)
5653+
{
5654+
return AssertQuery(
5655+
async,
5656+
ss => from l1 in ss.Set<Level1>().Include(l => l.OneToMany_Required1)
5657+
let level2Ids = from level2 in l1.OneToMany_Required1 select level2.Id
5658+
let level3s = (from level3 in ss.Set<Level3>()
5659+
where level2Ids.Contains(level3.Level2_Required_Id)
5660+
select level3).AsEnumerable()
5661+
from level3 in level3s.DefaultIfEmpty()
5662+
select new { l1, level3 },
5663+
elementSorter: e => (e.l1.Id, e.level3?.Id),
5664+
elementAsserter: (e, a) =>
5665+
{
5666+
AssertEqual(e.l1, a.l1);
5667+
AssertEqual(e.level3, a.level3);
5668+
});
5669+
}
56485670
}
56495671
}

test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs

+19
Original file line numberDiff line numberDiff line change
@@ -5897,6 +5897,25 @@ FROM [LevelOne] AS [l0]
58975897
WHERE [l0].[Id] < 3");
58985898
}
58995899

5900+
public override async Task Let_let_contains_from_outer_let(bool async)
5901+
{
5902+
await base.Let_let_contains_from_outer_let(async);
5903+
5904+
AssertSql(
5905+
@"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id]
5906+
FROM [LevelOne] AS [l]
5907+
OUTER APPLY (
5908+
SELECT [l0].[Id], [l0].[Level2_Optional_Id], [l0].[Level2_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse3Id], [l0].[OneToMany_Optional_Self_Inverse3Id], [l0].[OneToMany_Required_Inverse3Id], [l0].[OneToMany_Required_Self_Inverse3Id], [l0].[OneToOne_Optional_PK_Inverse3Id], [l0].[OneToOne_Optional_Self3Id]
5909+
FROM [LevelThree] AS [l0]
5910+
WHERE EXISTS (
5911+
SELECT 1
5912+
FROM [LevelTwo] AS [l1]
5913+
WHERE ([l].[Id] = [l1].[OneToMany_Required_Inverse2Id]) AND ([l1].[Id] = [l0].[Level2_Required_Id]))
5914+
) AS [t]
5915+
LEFT JOIN [LevelTwo] AS [l2] ON [l].[Id] = [l2].[OneToMany_Required_Inverse2Id]
5916+
ORDER BY [l].[Id], [t].[Id], [l2].[Id]");
5917+
}
5918+
59005919
private void AssertSql(params string[] expected)
59015920
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
59025921
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Threading.Tasks;
6+
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
57
using Xunit;
68

79
namespace Microsoft.EntityFrameworkCore.Query
@@ -19,36 +21,72 @@ public override Task Include_inside_subquery(bool async)
1921
return base.Include_inside_subquery(async);
2022
}
2123

22-
// Sqlite does not support cross/outer apply
23-
public override Task Filtered_include_after_different_filtered_include_different_level(bool async)
24-
=> null;
24+
public override async Task Filtered_include_after_different_filtered_include_different_level(bool async)
25+
=> Assert.Equal(
26+
SqliteStrings.ApplyNotSupported,
27+
(await Assert.ThrowsAsync<InvalidOperationException>(
28+
() => base.Filtered_include_after_different_filtered_include_different_level(async))).Message);
2529

26-
public override void Filtered_include_outer_parameter_used_inside_filter() { }
30+
public override void Filtered_include_outer_parameter_used_inside_filter()
31+
=> Assert.Equal(
32+
SqliteStrings.ApplyNotSupported,
33+
Assert.Throws<InvalidOperationException>(
34+
() => base.Filtered_include_outer_parameter_used_inside_filter()).Message);
2735

28-
public override Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(bool async)
29-
=> null;
36+
public override async Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(bool async)
37+
=> Assert.Equal(
38+
SqliteStrings.ApplyNotSupported,
39+
(await Assert.ThrowsAsync<InvalidOperationException>(
40+
() => base.Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(async))).Message);
3041

31-
public override Task Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(
42+
public override async Task Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(
3243
bool async)
33-
=> null;
44+
=> Assert.Equal(
45+
SqliteStrings.ApplyNotSupported,
46+
(await Assert.ThrowsAsync<InvalidOperationException>(
47+
() => base.Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(async))).Message);
3448

35-
public override Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(bool async)
36-
=> null;
49+
public override async Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(bool async)
50+
=> Assert.Equal(
51+
SqliteStrings.ApplyNotSupported,
52+
(await Assert.ThrowsAsync<InvalidOperationException>(
53+
() => base.Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(async))).Message);
3754

38-
public override Task Filtered_include_complex_three_level_with_middle_having_filter1(bool async)
39-
=> null;
55+
public override async Task Filtered_include_complex_three_level_with_middle_having_filter1(bool async)
56+
=> Assert.Equal(
57+
SqliteStrings.ApplyNotSupported,
58+
(await Assert.ThrowsAsync<InvalidOperationException>(
59+
() => base.Filtered_include_complex_three_level_with_middle_having_filter1(async))).Message);
4060

41-
public override Task Filtered_include_complex_three_level_with_middle_having_filter2(bool async)
42-
=> null;
61+
public override async Task Filtered_include_complex_three_level_with_middle_having_filter2(bool async)
62+
=> Assert.Equal(
63+
SqliteStrings.ApplyNotSupported,
64+
(await Assert.ThrowsAsync<InvalidOperationException>(
65+
() => base.Filtered_include_complex_three_level_with_middle_having_filter2(async))).Message);
4366

44-
public override Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation_split(bool async)
45-
=> null;
67+
public override async Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation_split(bool async)
68+
=> Assert.Equal(
69+
SqliteStrings.ApplyNotSupported,
70+
(await Assert.ThrowsAsync<InvalidOperationException>(
71+
() => base.Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation_split(async))).Message);
4672

47-
public override Task
73+
public override async Task
4874
Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only_split(bool async)
49-
=> null;
75+
=> Assert.Equal(
76+
SqliteStrings.ApplyNotSupported,
77+
(await Assert.ThrowsAsync<InvalidOperationException>(
78+
() => base.Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only_split(async))).Message);
5079

51-
public override Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes_split(bool async)
52-
=> null;
80+
public override async Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes_split(bool async)
81+
=> Assert.Equal(
82+
SqliteStrings.ApplyNotSupported,
83+
(await Assert.ThrowsAsync<InvalidOperationException>(
84+
() => base.Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes_split(async))).Message);
85+
86+
public override async Task Let_let_contains_from_outer_let(bool async)
87+
=> Assert.Equal(
88+
SqliteStrings.ApplyNotSupported,
89+
(await Assert.ThrowsAsync<InvalidOperationException>(
90+
() => base.Let_let_contains_from_outer_let(async))).Message);
5391
}
5492
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Threading.Tasks;
6+
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
7+
using Xunit;
58
using Xunit.Abstractions;
69

710
namespace Microsoft.EntityFrameworkCore.Query
@@ -14,24 +17,47 @@ public ComplexNavigationsWeakQuerySqliteTest(ComplexNavigationsWeakQuerySqliteFi
1417
{
1518
}
1619

17-
// Sqlite does not support cross/outer apply
18-
public override Task Filtered_include_after_different_filtered_include_different_level(bool async)
19-
=> null;
20-
21-
public override Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(bool async)
22-
=> null;
23-
24-
public override Task Filtered_include_complex_three_level_with_middle_having_filter1(bool async)
25-
=> null;
26-
27-
public override Task Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(
20+
public override async Task Filtered_include_after_different_filtered_include_different_level(bool async)
21+
=> Assert.Equal(
22+
SqliteStrings.ApplyNotSupported,
23+
(await Assert.ThrowsAsync<InvalidOperationException>(
24+
() => base.Filtered_include_after_different_filtered_include_different_level(async))).Message);
25+
26+
public override async Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(bool async)
27+
=> Assert.Equal(
28+
SqliteStrings.ApplyNotSupported,
29+
(await Assert.ThrowsAsync<InvalidOperationException>(
30+
() => base.Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(async))).Message);
31+
32+
public override async Task Filtered_include_complex_three_level_with_middle_having_filter1(bool async)
33+
=> Assert.Equal(
34+
SqliteStrings.ApplyNotSupported,
35+
(await Assert.ThrowsAsync<InvalidOperationException>(
36+
() => base.Filtered_include_complex_three_level_with_middle_having_filter1(async))).Message);
37+
38+
public override async Task Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(
2839
bool async)
29-
=> null;
30-
31-
public override Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(bool async)
32-
=> null;
33-
34-
public override Task Filtered_include_complex_three_level_with_middle_having_filter2(bool async)
35-
=> null;
40+
=> Assert.Equal(
41+
SqliteStrings.ApplyNotSupported,
42+
(await Assert.ThrowsAsync<InvalidOperationException>(
43+
() => base.Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(async))).Message);
44+
45+
public override async Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(bool async)
46+
=> Assert.Equal(
47+
SqliteStrings.ApplyNotSupported,
48+
(await Assert.ThrowsAsync<InvalidOperationException>(
49+
() => base.Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(async))).Message);
50+
51+
public override async Task Filtered_include_complex_three_level_with_middle_having_filter2(bool async)
52+
=> Assert.Equal(
53+
SqliteStrings.ApplyNotSupported,
54+
(await Assert.ThrowsAsync<InvalidOperationException>(
55+
() => base.Filtered_include_complex_three_level_with_middle_having_filter2(async))).Message);
56+
57+
public override async Task Let_let_contains_from_outer_let(bool async)
58+
=> Assert.Equal(
59+
SqliteStrings.ApplyNotSupported,
60+
(await Assert.ThrowsAsync<InvalidOperationException>(
61+
() => base.Let_let_contains_from_outer_let(async))).Message);
3662
}
3763
}

0 commit comments

Comments
 (0)