diff --git a/.github/workflows/auto-approve-codeflow.yml b/.github/workflows/auto-approve-codeflow.yml new file mode 100644 index 00000000000..21beab75fcc --- /dev/null +++ b/.github/workflows/auto-approve-codeflow.yml @@ -0,0 +1,138 @@ +name: Auto-approve dependency-only codeflow PRs + +on: + pull_request_target: + types: [opened, synchronize] + +permissions: + pull-requests: write + +jobs: + auto-approve-codeflow: + if: github.event.pull_request.user.login == 'dotnet-maestro[bot]' + runs-on: ubuntu-latest + steps: + - name: Validate and auto-approve + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + + REPO="${GITHUB_REPOSITORY}" + PR="${PR_NUMBER}" + + echo "Validating codeflow PR #${PR}..." + + # Get all changed files with their patches + files_json=$(gh api "repos/${REPO}/pulls/${PR}/files" --paginate | jq -s 'add') + file_count=$(echo "$files_json" | jq 'length') + echo "PR modifies ${file_count} file(s)" + + if [[ "$file_count" -eq 0 ]]; then + echo "::error::PR has no changed files" + exit 1 + fi + + for ((i = 0; i < file_count; i++)); do + filename=$(echo "$files_json" | jq -r ".[$i].filename") + status=$(echo "$files_json" | jq -r ".[$i].status") + patch=$(echo "$files_json" | jq -r ".[$i].patch // \"\"") + + echo "::group::Validating ${filename}" + + # 1. File must be in the allowed set + case "$filename" in + NuGet.config|eng/Version.Details.xml|eng/Version.Details.props|global.json) + ;; + *) + echo "::error::File '${filename}' is not in the allowed set for dependency-only codeflow PRs" + exit 1 + ;; + esac + + # 2. Only modified files are expected + if [[ "$status" != "modified" ]]; then + echo "::error::File '${filename}' has status '${status}' (expected 'modified')" + exit 1 + fi + + # 3. Patch data must be present + if [[ -z "$patch" ]]; then + echo "::error::File '${filename}' has no patch data" + exit 1 + fi + + # 4. Validate each changed line in the diff + while IFS= read -r line; do + [[ -z "$line" ]] && continue + + # Only check added (+) and removed (-) lines + if [[ "$line" != +* && "$line" != -* ]]; then + continue + fi + + # Get the content (strip the +/- prefix) + content="${line:1}" + + # Skip empty/whitespace-only lines + if [[ "$content" =~ ^[[:space:]]*$ ]]; then + continue + fi + + case "$filename" in + "eng/Version.Details.xml") + # Allowed: element with Sha/BarId, elements, opening tags with Version + if [[ "$content" =~ ^[[:space:]]*\ ]]; then + : + elif [[ "$content" =~ ^[[:space:]]*\[0-9a-f]+\ ]]; then + : + elif [[ "$content" =~ ^[[:space:]]*\value + if [[ "$content" =~ ^[[:space:]]*\<[A-Za-z0-9]+Version\>[^<]+\ ]]; then + : + else + echo "::error::Unexpected change in ${filename}: ${content}" + exit 1 + fi + ;; + + "global.json") + # Allowed: msbuild-sdks version entries only + if [[ "$content" =~ ^[[:space:]]*\"Microsoft\.DotNet\.(Arcade|Helix)\.Sdk\":[[:space:]]*\"[^\"]+\" ]]; then + : + else + echo "::error::Unexpected change in ${filename}: ${content}" + exit 1 + fi + ;; + + "NuGet.config") + # Allowed: darc-* package source entries pointing to Azure DevOps NuGet feeds + if [[ "$content" =~ ^[[:space:]]*\ ]]; then + : + else + echo "::error::Unexpected change in ${filename}: ${content}" + exit 1 + fi + ;; + esac + done <<< "$patch" + + echo " ✅ Validated" + echo "::endgroup::" + done + + echo "" + echo "All files contain only expected dependency update changes." + echo "Approving PR..." + gh pr review "$PR" --repo "$REPO" --approve \ + --body "Auto-approved: this codeflow PR contains only dependency version updates." diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs index 7d81f873b79..24a41424f1c 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs @@ -286,7 +286,7 @@ protected override Expression VisitExtension(Expression expression) var navigation = collectionResultExpression.StructuralProperty switch { INavigationBase n => n, - null => null, + null or IComplexProperty => null, _ => throw new UnreachableException() }; diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesProjectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesProjectionCosmosTest.cs index 74a4959debb..a18ecdc8a38 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesProjectionCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesProjectionCosmosTest.cs @@ -296,6 +296,9 @@ FROM root c #region Subquery + public override Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + => AssertTranslationFailed(() => base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior)); + public override Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) => AssertTranslationFailed(() => base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior)); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs index fc8e9d376d6..5ac5fcea75f 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs @@ -333,6 +333,14 @@ public override async Task Select_associate_and_target_to_index_based_binding_vi #region Subquery + public override async Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + { + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + await AssertTranslationFailed(() => base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior)); + } + } + public override async Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) { if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) diff --git a/test/EFCore.Specification.Tests/Query/Associations/AssociationsProjectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Associations/AssociationsProjectionTestBase.cs index cd6b9cb18b9..ce416d4e449 100644 --- a/test/EFCore.Specification.Tests/Query/Associations/AssociationsProjectionTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Associations/AssociationsProjectionTestBase.cs @@ -304,6 +304,18 @@ public virtual Task Select_subquery_optional_related_FirstOrDefault(QueryTrackin .FirstOrDefault()!.RequiredNestedAssociate), queryTrackingBehavior: queryTrackingBehavior); + [ConditionalTheory, MemberData(nameof(TrackingData))] + public virtual Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + => AssertQuery( + ss => ss.Set() + .OrderBy(x => x.Id) + .Select(x => ss.Set() + .OrderBy(e => e.Id) + .FirstOrDefault()!.AssociateCollection), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: r => r.Id), + queryTrackingBehavior: queryTrackingBehavior); + // [ConditionalTheory] // [MemberData(nameof(TrackingData))] // public virtual Task Select_subquery_root_set_trunk_FirstOrDefault_collection(QueryTrackingBehavior queryTrackingBehavior) diff --git a/test/EFCore.Specification.Tests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionTestBase.cs index baa74aa95fe..0abf4c9ddce 100644 --- a/test/EFCore.Specification.Tests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionTestBase.cs @@ -167,6 +167,10 @@ public override Task Select_associate_and_target_to_index_based_binding_via_clos #region Subquery + public override Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery( + queryTrackingBehavior, () => base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior)); + public override Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) => AssertOwnedTrackingQuery( queryTrackingBehavior, () => base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior)); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonProjectionSqlServerTest.cs index 549b3af7a9f..82a6926bd2f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonProjectionSqlServerTest.cs @@ -406,6 +406,23 @@ FROM [RootEntity] AS [r] #region Subquery + public override async Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior); + + AssertSql( + """ +SELECT [r1].[c], [r1].[c0] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [r0].[AssociateCollection] AS [c], 1 AS [c0] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +ORDER BY [r].[Id] +"""); + } + public override async Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) { await base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingProjectionSqlServerTest.cs index 255c8880865..ffb9c0f2077 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingProjectionSqlServerTest.cs @@ -264,6 +264,23 @@ FROM [RootEntity] AS [r] #region Subquery + public override async Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior); + + AssertSql( + """ +SELECT [r1].[Id], [r1].[Name], [r1].[OptionalAssociate_Id], [r1].[OptionalAssociate_Int], [r1].[OptionalAssociate_Ints], [r1].[OptionalAssociate_Name], [r1].[OptionalAssociate_String], [r1].[OptionalAssociate_OptionalNestedAssociate_Id], [r1].[OptionalAssociate_OptionalNestedAssociate_Int], [r1].[OptionalAssociate_OptionalNestedAssociate_Ints], [r1].[OptionalAssociate_OptionalNestedAssociate_Name], [r1].[OptionalAssociate_OptionalNestedAssociate_String], [r1].[OptionalAssociate_RequiredNestedAssociate_Id], [r1].[OptionalAssociate_RequiredNestedAssociate_Int], [r1].[OptionalAssociate_RequiredNestedAssociate_Ints], [r1].[OptionalAssociate_RequiredNestedAssociate_Name], [r1].[OptionalAssociate_RequiredNestedAssociate_String], [r1].[RequiredAssociate_Id], [r1].[RequiredAssociate_Int], [r1].[RequiredAssociate_Ints], [r1].[RequiredAssociate_Name], [r1].[RequiredAssociate_String], [r1].[RequiredAssociate_OptionalNestedAssociate_Id], [r1].[RequiredAssociate_OptionalNestedAssociate_Int], [r1].[RequiredAssociate_OptionalNestedAssociate_Ints], [r1].[RequiredAssociate_OptionalNestedAssociate_Name], [r1].[RequiredAssociate_OptionalNestedAssociate_String], [r1].[RequiredAssociate_RequiredNestedAssociate_Id], [r1].[RequiredAssociate_RequiredNestedAssociate_Int], [r1].[RequiredAssociate_RequiredNestedAssociate_Ints], [r1].[RequiredAssociate_RequiredNestedAssociate_Name], [r1].[RequiredAssociate_RequiredNestedAssociate_String], [r1].[c] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [r0].[Id], [r0].[Name], [r0].[OptionalAssociate_Id], [r0].[OptionalAssociate_Int], [r0].[OptionalAssociate_Ints], [r0].[OptionalAssociate_Name], [r0].[OptionalAssociate_String], [r0].[OptionalAssociate_OptionalNestedAssociate_Id], [r0].[OptionalAssociate_OptionalNestedAssociate_Int], [r0].[OptionalAssociate_OptionalNestedAssociate_Ints], [r0].[OptionalAssociate_OptionalNestedAssociate_Name], [r0].[OptionalAssociate_OptionalNestedAssociate_String], [r0].[OptionalAssociate_RequiredNestedAssociate_Id], [r0].[OptionalAssociate_RequiredNestedAssociate_Int], [r0].[OptionalAssociate_RequiredNestedAssociate_Ints], [r0].[OptionalAssociate_RequiredNestedAssociate_Name], [r0].[OptionalAssociate_RequiredNestedAssociate_String], [r0].[RequiredAssociate_Id], [r0].[RequiredAssociate_Int], [r0].[RequiredAssociate_Ints], [r0].[RequiredAssociate_Name], [r0].[RequiredAssociate_String], [r0].[RequiredAssociate_OptionalNestedAssociate_Id], [r0].[RequiredAssociate_OptionalNestedAssociate_Int], [r0].[RequiredAssociate_OptionalNestedAssociate_Ints], [r0].[RequiredAssociate_OptionalNestedAssociate_Name], [r0].[RequiredAssociate_OptionalNestedAssociate_String], [r0].[RequiredAssociate_RequiredNestedAssociate_Id], [r0].[RequiredAssociate_RequiredNestedAssociate_Int], [r0].[RequiredAssociate_RequiredNestedAssociate_Ints], [r0].[RequiredAssociate_RequiredNestedAssociate_Name], [r0].[RequiredAssociate_RequiredNestedAssociate_String], 1 AS [c] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +ORDER BY [r].[Id] +"""); + } + public override async Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) { await base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/Navigations/NavigationsProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/Navigations/NavigationsProjectionSqlServerTest.cs index e0164642438..2403bae3bdc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/Navigations/NavigationsProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/Navigations/NavigationsProjectionSqlServerTest.cs @@ -369,6 +369,30 @@ FROM [RootEntity] AS [r] #region Subquery + public override async Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior); + + AssertSql( + """ +SELECT [r].[Id], [r1].[Id], [s].[Id], [s].[CollectionRootId], [s].[Int], [s].[Ints], [s].[Name], [s].[OptionalNestedAssociateId], [s].[RequiredNestedAssociateId], [s].[String], [s].[Id0], [s].[Id1], [s].[Id2], [s].[CollectionAssociateId], [s].[Int0], [s].[Ints0], [s].[Name0], [s].[String0], [s].[CollectionAssociateId0], [s].[Int1], [s].[Ints1], [s].[Name1], [s].[String1], [s].[CollectionAssociateId1], [s].[Int2], [s].[Ints2], [s].[Name2], [s].[String2], [r1].[c] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) 1 AS [c], [r0].[Id] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +LEFT JOIN ( + SELECT [a].[Id], [a].[CollectionRootId], [a].[Int], [a].[Ints], [a].[Name], [a].[OptionalNestedAssociateId], [a].[RequiredNestedAssociateId], [a].[String], [n].[Id] AS [Id0], [n0].[Id] AS [Id1], [n1].[Id] AS [Id2], [n1].[CollectionAssociateId], [n1].[Int] AS [Int0], [n1].[Ints] AS [Ints0], [n1].[Name] AS [Name0], [n1].[String] AS [String0], [n].[CollectionAssociateId] AS [CollectionAssociateId0], [n].[Int] AS [Int1], [n].[Ints] AS [Ints1], [n].[Name] AS [Name1], [n].[String] AS [String1], [n0].[CollectionAssociateId] AS [CollectionAssociateId1], [n0].[Int] AS [Int2], [n0].[Ints] AS [Ints2], [n0].[Name] AS [Name2], [n0].[String] AS [String2] + FROM [AssociateType] AS [a] + LEFT JOIN [NestedAssociateType] AS [n] ON [a].[OptionalNestedAssociateId] = [n].[Id] + INNER JOIN [NestedAssociateType] AS [n0] ON [a].[RequiredNestedAssociateId] = [n0].[Id] + LEFT JOIN [NestedAssociateType] AS [n1] ON [a].[Id] = [n1].[CollectionAssociateId] +) AS [s] ON [r1].[Id] = [s].[CollectionRootId] +ORDER BY [r].[Id], [r1].[Id], [s].[Id], [s].[Id0], [s].[Id1] +"""); + } + public override async Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) { await base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonProjectionSqlServerTest.cs index ec482511cea..a58dcb921a8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonProjectionSqlServerTest.cs @@ -451,6 +451,26 @@ FROM [RootEntity] AS [r] #region Subquery + public override async Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r1].[c], [r1].[Id], [r1].[c0] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [r0].[AssociateCollection] AS [c], [r0].[Id], 1 AS [c0] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +ORDER BY [r].[Id] +"""); + } + } + public override async Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) { await base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionSqlServerTest.cs index 4d7646cf9b0..8b57be8d69c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsProjectionSqlServerTest.cs @@ -414,6 +414,33 @@ FROM [RootEntity] AS [r] #region Subquery + public override async Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r].[Id], [r5].[Id], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Ints], [s].[Name], [s].[String], [s].[AssociateTypeRootEntityId], [s].[AssociateTypeId], [s].[AssociateTypeRootEntityId0], [s].[AssociateTypeId0], [s].[AssociateTypeRootEntityId1], [s].[AssociateTypeId1], [s].[Id0], [s].[Int0], [s].[Ints0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Ints1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Ints2], [s].[Name2], [s].[String2], [r5].[c] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) 1 AS [c], [r0].[Id] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r5] +LEFT JOIN ( + SELECT [r1].[RootEntityId], [r1].[Id], [r1].[Int], [r1].[Ints], [r1].[Name], [r1].[String], [r2].[AssociateTypeRootEntityId], [r2].[AssociateTypeId], [r3].[AssociateTypeRootEntityId] AS [AssociateTypeRootEntityId0], [r3].[AssociateTypeId] AS [AssociateTypeId0], [r4].[AssociateTypeRootEntityId] AS [AssociateTypeRootEntityId1], [r4].[AssociateTypeId] AS [AssociateTypeId1], [r4].[Id] AS [Id0], [r4].[Int] AS [Int0], [r4].[Ints] AS [Ints0], [r4].[Name] AS [Name0], [r4].[String] AS [String0], [r2].[Id] AS [Id1], [r2].[Int] AS [Int1], [r2].[Ints] AS [Ints1], [r2].[Name] AS [Name1], [r2].[String] AS [String1], [r3].[Id] AS [Id2], [r3].[Int] AS [Int2], [r3].[Ints] AS [Ints2], [r3].[Name] AS [Name2], [r3].[String] AS [String2] + FROM [RelatedCollection] AS [r1] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r2] ON [r1].[RootEntityId] = [r2].[AssociateTypeRootEntityId] AND [r1].[Id] = [r2].[AssociateTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r3] ON [r1].[RootEntityId] = [r3].[AssociateTypeRootEntityId] AND [r1].[Id] = [r3].[AssociateTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r4] ON [r1].[RootEntityId] = [r4].[AssociateTypeRootEntityId] AND [r1].[Id] = [r4].[AssociateTypeId] +) AS [s] ON [r5].[Id] = [s].[RootEntityId] +ORDER BY [r].[Id], [r5].[Id], [s].[RootEntityId], [s].[Id], [s].[AssociateTypeRootEntityId], [s].[AssociateTypeId], [s].[AssociateTypeRootEntityId0], [s].[AssociateTypeId0], [s].[AssociateTypeRootEntityId1], [s].[AssociateTypeId1] +"""); + } + } + public override async Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) { await base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingProjectionSqlServerTest.cs index 64b88138ac7..7ac9f81e98e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingProjectionSqlServerTest.cs @@ -372,6 +372,31 @@ FROM [RootEntity] AS [r] #region Subquery + public override async Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r].[Id], [r3].[Id], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Ints], [s].[Name], [s].[String], [s].[AssociateTypeRootEntityId], [s].[AssociateTypeId], [s].[Id0], [s].[Int0], [s].[Ints0], [s].[Name0], [s].[String0], [s].[OptionalNestedAssociate_Id], [s].[OptionalNestedAssociate_Int], [s].[OptionalNestedAssociate_Ints], [s].[OptionalNestedAssociate_Name], [s].[OptionalNestedAssociate_String], [s].[RequiredNestedAssociate_Id], [s].[RequiredNestedAssociate_Int], [s].[RequiredNestedAssociate_Ints], [s].[RequiredNestedAssociate_Name], [s].[RequiredNestedAssociate_String], [r3].[c] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) 1 AS [c], [r0].[Id] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r3] +LEFT JOIN ( + SELECT [r1].[RootEntityId], [r1].[Id], [r1].[Int], [r1].[Ints], [r1].[Name], [r1].[String], [r2].[AssociateTypeRootEntityId], [r2].[AssociateTypeId], [r2].[Id] AS [Id0], [r2].[Int] AS [Int0], [r2].[Ints] AS [Ints0], [r2].[Name] AS [Name0], [r2].[String] AS [String0], [r1].[OptionalNestedAssociate_Id], [r1].[OptionalNestedAssociate_Int], [r1].[OptionalNestedAssociate_Ints], [r1].[OptionalNestedAssociate_Name], [r1].[OptionalNestedAssociate_String], [r1].[RequiredNestedAssociate_Id], [r1].[RequiredNestedAssociate_Int], [r1].[RequiredNestedAssociate_Ints], [r1].[RequiredNestedAssociate_Name], [r1].[RequiredNestedAssociate_String] + FROM [RelatedCollection] AS [r1] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r2] ON [r1].[RootEntityId] = [r2].[AssociateTypeRootEntityId] AND [r1].[Id] = [r2].[AssociateTypeId] +) AS [s] ON [r3].[Id] = [s].[RootEntityId] +ORDER BY [r].[Id], [r3].[Id], [s].[RootEntityId], [s].[Id], [s].[AssociateTypeRootEntityId], [s].[AssociateTypeId] +"""); + } + } + public override async Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) { await base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonProjectionSqliteTest.cs index a342afd4f43..2bc50888559 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonProjectionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonProjectionSqliteTest.cs @@ -17,6 +17,9 @@ public override Task SelectMany_nested_collection_on_required_associate(QueryTra public override Task SelectMany_nested_collection_on_optional_associate(QueryTrackingBehavior queryTrackingBehavior) => AssertApplyNotSupported(() => base.SelectMany_nested_collection_on_optional_associate(queryTrackingBehavior)); + public override Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior)); + public override Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) => AssertApplyNotSupported(() => base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior)); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs index bd2aa117b62..de04e4e6c43 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs @@ -8,6 +8,9 @@ namespace Microsoft.EntityFrameworkCore.Query.Associations.ComplexTableSplitting public class ComplexTableSplittingProjectionSqliteTest(ComplexTableSplittingSqliteFixture fixture, ITestOutputHelper testOutputHelper) : ComplexTableSplittingProjectionRelationalTestBase(fixture, testOutputHelper) { + public override Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior)); + public override Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) => AssertApplyNotSupported(() => base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior)); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Associations/Navigations/NavigationsProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Associations/Navigations/NavigationsProjectionSqliteTest.cs index 287c5f590ed..d5e87a09320 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Associations/Navigations/NavigationsProjectionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Associations/Navigations/NavigationsProjectionSqliteTest.cs @@ -8,6 +8,9 @@ namespace Microsoft.EntityFrameworkCore.Query.Associations.Navigations; public class NavigationsProjectionSqliteTest(NavigationsSqliteFixture fixture, ITestOutputHelper testOutputHelper) : NavigationsProjectionRelationalTestBase(fixture, testOutputHelper) { + public override Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior)); + public override Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) => AssertApplyNotSupported(() => base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior)); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonProjectionSqliteTest.cs index a09bb692310..0a534560a9a 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonProjectionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonProjectionSqliteTest.cs @@ -23,6 +23,11 @@ public override Task SelectMany_nested_collection_on_optional_associate(QueryTra ? Task.CompletedTask // Base test expects "can't track owned entities" exception, but with SQLite we get "no CROSS APPLY" : AssertApplyNotSupported(() => base.SelectMany_nested_collection_on_optional_associate(queryTrackingBehavior)); + public override Task Select_subquery_FirstOrDefault_complex_collection(QueryTrackingBehavior queryTrackingBehavior) + => queryTrackingBehavior is QueryTrackingBehavior.TrackAll + ? Task.CompletedTask // Base test expects "can't track owned entities" exception, but with SQLite we get "no CROSS APPLY" + : AssertApplyNotSupported(() => base.Select_subquery_FirstOrDefault_complex_collection(queryTrackingBehavior)); + public override Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) => queryTrackingBehavior is QueryTrackingBehavior.TrackAll ? Task.CompletedTask // Base test expects "can't track owned entities" exception, but with SQLite we get "no CROSS APPLY"