From 3c0a65a849d00582fd55b3c729cc5df26ae20ae2 Mon Sep 17 00:00:00 2001 From: Maurycy Markowski Date: Tue, 1 Sep 2020 10:09:51 -0700 Subject: [PATCH] Fix to #15873 - Query: Identifying columns in the case of distinct (#21990) Added validation step for AddCollectionJoin which checks that if subquery contains Distinct or GroupBy, the projection contains all identifying columns needed to correctly bucket the results during materialization. Also making sure that identifying columns can be correctly propagated during pushdown and joining - if they are not we mark them as such (by removing identifying columns altogether), so that we can throw exception when these columns are actually needed. Fixes #15873 Fixes #20184 --- .../Properties/RelationalStrings.Designer.cs | 18 ++- .../Properties/RelationalStrings.resx | 9 +- .../Query/SqlExpressions/SelectExpression.cs | 76 +++++++-- .../Query/NorthwindSelectQueryCosmosTest.cs | 6 +- .../Query/OwnedQueryCosmosTest.cs | 8 + .../Query/OwnedQueryInMemoryTest.cs | 8 + .../GearsOfWarQueryRelationalTestBase.cs | 41 +++++ ...NorthwindGroupByQueryRelationalTestBase.cs | 81 ++++++++++ ...dKeylessEntitiesQueryRelationalTestBase.cs | 18 ++- .../Query/UdfDbFunctionTestBase.cs | 147 +++++++----------- .../Query/NorthwindGroupByQueryTestBase.cs | 63 ++++---- .../Query/NorthwindSelectQueryTestBase.cs | 2 +- .../Query/OwnedQueryTestBase.cs | 19 +++ ...omplexNavigationsWeakQuerySqlServerTest.cs | 11 ++ .../NorthwindGroupByQuerySqlServerTest.cs | 45 +++++- ...orthwindMiscellaneousQuerySqlServerTest.cs | 27 ++++ .../NorthwindSelectQuerySqlServerTest.cs | 21 +-- .../Query/OwnedQuerySqlServerTest.cs | 12 ++ .../Query/UdfDbFunctionSqlServerTests.cs | 81 ---------- .../ComplexNavigationsWeakQuerySqliteTest.cs | 9 ++ .../Query/NorthwindGroupByQuerySqliteTest.cs | 16 ++ .../Query/NorthwindSelectQuerySqliteTest.cs | 6 +- 22 files changed, 478 insertions(+), 246 deletions(-) diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index 5991938abef..0c0cf4315ce 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -591,6 +591,12 @@ public static string InsertDataOperationValuesCountMismatch([CanBeNull] object v GetString("InsertDataOperationValuesCountMismatch", nameof(valuesCount), nameof(columnsCount), nameof(table)), valuesCount, columnsCount, table); + /// + /// Not enough information to uniquely identify outer element in correlated collection scenario. This can happen when trying to correlate on keyless entity or when using 'Distinct' or 'GroupBy' operations without projecting all of the key columns. + /// + public static string InsufficientInformationToIdentifyOuterElementOfCollectionJoin + => GetString("InsufficientInformationToIdentifyOuterElementOfCollectionJoin"); + /// /// The specified CommandTimeout value is not valid. It must be a positive number. /// @@ -687,6 +693,12 @@ public static string MissingConcurrencyColumn([CanBeNull] object entityType, [Ca GetString("MissingConcurrencyColumn", nameof(entityType), nameof(missingColumn), nameof(table)), entityType, missingColumn, table); + /// + /// Collection subquery that uses 'Distinct' or 'Group By' operations must project key columns of all of it's tables. Missing column: {column}. Either add column(s) to the projection or rewrite query to not use 'GroupBy'/'Distinct' operation. + /// + public static string MissingIdentifyingProjectionInDistinctGroupBySubquery([CanBeNull] object column) + => string.Format(GetString("MissingIdentifyingProjectionInDistinctGroupBySubquery", nameof(column)), column); + /// /// Reverse could not be translated to the server because there is no ordering on the server side. /// @@ -817,12 +829,6 @@ public static string ParameterNotObjectArray([CanBeNull] object parameter) public static string PendingAmbientTransaction => GetString("PendingAmbientTransaction"); - /// - /// Projecting collection correlated with keyless entity is not supported. - /// - public static string ProjectingCollectionOnKeylessEntityNotSupported - => GetString("ProjectingCollectionOnKeylessEntityNotSupported"); - /// /// Different projection mapping count in set operation. /// diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index a406425c9dd..ac3476f4bdb 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -337,6 +337,9 @@ The number of values ({valuesCount}) doesn't match the number of columns ({columnsCount}) for the data insertion operation on '{table}'. Provide the same number of values and columns. + + Not enough information to uniquely identify outer element in correlated collection scenario. This can happen when trying to correlate on keyless entity or when using 'Distinct' or 'GroupBy' operations without projecting all of the key columns. + The specified CommandTimeout value is not valid. It must be a positive number. @@ -592,6 +595,9 @@ Entity type '{entityType}' doesn't contain a property mapped to the store-generated concurrency token column '{missingColumn}' that is used by another entity type sharing the table '{table}'. Add a store-generated property mapped to the same column to '{entityType}'. It can be in shadow state. + + Collection subquery that uses 'Distinct' or 'Group By' operations must project key columns of all of it's tables. Missing column: {column}. Either add column(s) to the projection or rewrite query to not use 'GroupBy'/'Distinct' operation. + Reverse could not be translated to the server because there is no ordering on the server side. @@ -649,9 +655,6 @@ This connection was used with an ambient transaction. The original ambient transaction needs to be completed before this connection can be used outside of it. - - Projecting collection correlated with keyless entity is not supported. - Different projection mapping count in set operation. diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index d87b4f2bef9..83fd4fb7d58 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -1101,7 +1101,7 @@ public IDictionary PushdownIntoSubquery() var identifiers = _identifier.ToList(); _identifier.Clear(); - // TODO: See issue#15873 + foreach (var identifier in identifiers) { if (projectionMap.TryGetValue(identifier.Column, out var outerColumn)) @@ -1109,16 +1109,24 @@ public IDictionary PushdownIntoSubquery() _identifier.Add((outerColumn, identifier.Comparer)); } else if (!IsDistinct - && GroupBy.Count == 0) + && GroupBy.Count == 0 + || (GroupBy.Contains(identifier.Column))) { outerColumn = subquery.GenerateOuterColumn(identifier.Column); _identifier.Add((outerColumn, identifier.Comparer)); } + else + { + // if we can't propagate any identifier - clear them all instead + // when adding collection join we detect this and throw appropriate exception + _identifier.Clear(); + break; + } } var childIdentifiers = _childIdentifiers.ToList(); _childIdentifiers.Clear(); - // TODO: See issue#15873 + foreach (var identifier in childIdentifiers) { if (projectionMap.TryGetValue(identifier.Column, out var outerColumn)) @@ -1126,11 +1134,19 @@ public IDictionary PushdownIntoSubquery() _childIdentifiers.Add((outerColumn, identifier.Comparer)); } else if (!IsDistinct - && GroupBy.Count == 0) + && GroupBy.Count == 0 + || (GroupBy.Contains(identifier.Column))) { outerColumn = subquery.GenerateOuterColumn(identifier.Column); _childIdentifiers.Add((outerColumn, identifier.Comparer)); } + else + { + // if we can't propagate any identifier - clear them all instead + // when adding collection join we detect this and throw appropriate exception + _childIdentifiers.Clear(); + break; + } } var pendingCollections = _pendingCollections.ToList(); @@ -1361,6 +1377,12 @@ public Expression ApplyCollectionJoin( var innerSelectExpression = _pendingCollections[collectionIndex]; _pendingCollections[collectionIndex] = null; + if (_identifier.Count == 0 + || innerSelectExpression._identifier.Count == 0) + { + throw new InvalidOperationException(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin); + } + if (splitQuery) { var containsReferenceToOuter = new SelectExpressionCorrelationFindingExpressionVisitor(this) @@ -1389,6 +1411,8 @@ public Expression ApplyCollectionJoin( var parentIdentifier = GetIdentifierAccessor(identifierFromParent).Item1; innerSelectExpression.ApplyProjection(); + ValidateIdentifyingProjection(innerSelectExpression); + for (var i = 0; i < identifierFromParent.Count; i++) { AppendOrdering(new OrderingExpression(identifierFromParent[i].Column, ascending: true)); @@ -1483,16 +1507,14 @@ public Expression ApplyCollectionJoin( else { var parentIdentifierList = _identifier.Except(_childIdentifiers).ToList(); - if (parentIdentifierList.Count == 0) - { - throw new InvalidOperationException(RelationalStrings.ProjectingCollectionOnKeylessEntityNotSupported); - } var (parentIdentifier, parentIdentifierValueComparers) = GetIdentifierAccessor(parentIdentifierList); var (outerIdentifier, outerIdentifierValueComparers) = GetIdentifierAccessor(_identifier); var innerClientEval = innerSelectExpression.Projection.Count > 0; innerSelectExpression.ApplyProjection(); + ValidateIdentifyingProjection(innerSelectExpression); + if (collectionIndex == 0) { foreach (var identifier in parentIdentifierList) @@ -1614,6 +1636,24 @@ public Expression ApplyCollectionJoin( return result; } + + static void ValidateIdentifyingProjection(SelectExpression selectExpression) + { + if (selectExpression.IsDistinct + || selectExpression.GroupBy.Count > 0) + { + var innerSelectProjectionExpressions = selectExpression._projection.Select(p => p.Expression).ToList(); + foreach (var innerSelectIdentifier in selectExpression._identifier) + { + if (!innerSelectProjectionExpressions.Contains(innerSelectIdentifier.Column) + && (selectExpression.GroupBy.Count == 0 + || !selectExpression.GroupBy.Contains(innerSelectIdentifier.Column))) + + throw new InvalidOperationException(RelationalStrings.MissingIdentifyingProjectionInDistinctGroupBySubquery( + innerSelectIdentifier.Column.Table.Alias + "." + innerSelectIdentifier.Column.Name)); + } + } + } } private sealed class EntityShaperNullableMarkingExpressionVisitor : ExpressionVisitor @@ -2247,14 +2287,24 @@ private void AddJoin( .Remap(joinPredicate); } - if (joinType == JoinType.LeftJoin - || joinType == JoinType.OuterApply) + if (_identifier.Count > 0 + && innerSelectExpression._identifier.Count > 0) { - _identifier.AddRange(innerSelectExpression._identifier.Select(e => (e.Column.MakeNullable(), e.Comparer))); + if (joinType == JoinType.LeftJoin + || joinType == JoinType.OuterApply) + { + _identifier.AddRange(innerSelectExpression._identifier.Select(e => (e.Column.MakeNullable(), e.Comparer))); + } + else + { + _identifier.AddRange(innerSelectExpression._identifier); + } } - else + else if (innerSelectExpression._identifier.Count == 0) { - _identifier.AddRange(innerSelectExpression._identifier); + // if the subquery that is joined to can't be uniquely identified + // then the entire join should also not be marked as non-identifiable + _identifier.Clear(); } var innerTable = innerSelectExpression.Tables.Single(); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs index 308675c97b1..7434ac6b008 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs @@ -1143,9 +1143,11 @@ public override Task Projecting_multiple_collection_with_same_constant_works(boo } [ConditionalTheory(Skip = "Issue#17246")] - public override Task Projecting_after_navigation_and_distinct_works_correctly(bool async) + public override async Task Projecting_after_navigation_and_distinct_throws(bool isAsync) { - return base.Projecting_after_navigation_and_distinct_works_correctly(async); + await base.Projecting_after_navigation_and_distinct_throws(isAsync); + + AssertSql(" "); } public override Task Reverse_without_explicit_ordering_throws(bool async) diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs index 49c8f4fa429..b25dc49866f 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs @@ -482,6 +482,14 @@ public override async Task Query_on_collection_entry_works_for_owned_collection( AssertSql(" "); } + [ConditionalTheory(Skip = "issue #17246")] + public override async Task Projecting_collection_correlated_with_keyless_entity_after_navigation_works_using_parent_identifiers(bool isAsync) + { + await base.Projecting_collection_correlated_with_keyless_entity_after_navigation_works_using_parent_identifiers(isAsync); + + AssertSql(" "); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.InMemory.FunctionalTests/Query/OwnedQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/OwnedQueryInMemoryTest.cs index 28e9dd18c8a..32e7642114e 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/OwnedQueryInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/OwnedQueryInMemoryTest.cs @@ -1,7 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit; using Xunit.Abstractions; namespace Microsoft.EntityFrameworkCore.Query @@ -14,6 +16,12 @@ public OwnedQueryInMemoryTest(OwnedQueryInMemoryFixture fixture, ITestOutputHelp //TestLoggerFactory.TestOutputHelper = testOutputHelper; } + [ConditionalTheory(Skip = "issue #19742")] + public override Task Projecting_collection_correlated_with_keyless_entity_after_navigation_works_using_parent_identifiers(bool async) + { + return base.Projecting_collection_correlated_with_keyless_entity_after_navigation_works_using_parent_identifiers(async); + } + public class OwnedQueryInMemoryFixture : OwnedQueryFixtureBase { protected override ITestStoreFactory TestStoreFactory diff --git a/test/EFCore.Relational.Specification.Tests/Query/GearsOfWarQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/GearsOfWarQueryRelationalTestBase.cs index 20f516316ea..f92650c4ca4 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/GearsOfWarQueryRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/GearsOfWarQueryRelationalTestBase.cs @@ -1,7 +1,13 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel; using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit; namespace Microsoft.EntityFrameworkCore.Query { @@ -13,6 +19,41 @@ protected GearsOfWarQueryRelationalTestBase(TFixture fixture) { } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Correlated_collection_with_Distinct_missing_indentifying_columns_in_projection(bool async) + { + var message = (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set() + .OrderBy(g => g.Nickname) + .Select(g => g.Weapons.SelectMany(x => x.Owner.AssignedCity.BornGears) + .Select(x => (bool?)x.HasSoulPatch).Distinct().ToList())))).Message; + + Assert.Equal(RelationalStrings.MissingIdentifyingProjectionInDistinctGroupBySubquery("w.Id"), message); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Correlated_collection_with_GroupBy_missing_indentifying_columns_in_projection(bool async) + { + var message = (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set() + .Select(m => new + { + m.Id, + grouping = m.ParticipatingSquads + .Select(ps => ps.SquadId) + .GroupBy(s => s) + .Select(g => new { g.Key, Count = g.Count() }) + })))).Message; + + Assert.Equal(RelationalStrings.MissingIdentifyingProjectionInDistinctGroupBySubquery("s.MissionId"), message); + } + protected virtual bool CanExecuteQueryString => false; diff --git a/test/EFCore.Relational.Specification.Tests/Query/NorthwindGroupByQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/NorthwindGroupByQueryRelationalTestBase.cs index 25878619825..fe9f724f3c0 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/NorthwindGroupByQueryRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/NorthwindGroupByQueryRelationalTestBase.cs @@ -1,7 +1,13 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.TestModels.Northwind; using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit; namespace Microsoft.EntityFrameworkCore.Query { @@ -13,6 +19,81 @@ protected NorthwindGroupByQueryRelationalTestBase(TFixture fixture) { } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Select_uncorrelated_collection_with_groupby_when_outer_is_distinct(bool async) + { + var message = (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set() + .Where(c => c.CustomerID.StartsWith("A")) + .Select(c => c.Customer.City) + .Distinct() + .Select(c => new + { + c1 = ss.Set().GroupBy(p => p.ProductID).Select(g => g.Key).ToArray(), + c2 = ss.Set().GroupBy(p => p.ProductID).Select(g => g.Count()).ToArray() + }), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertCollection(e.c1, a.c1); + AssertCollection(e.c2, a.c2); + }))).Message; + + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); + } + + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Complex_query_with_groupBy_in_subquery4(bool async) + { + var message = (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set() + .Select( + c => new + { + Key = c.CustomerID, + Subquery = c.Orders + .Select(o => new { First = o.OrderID, Second = o.Customer.City + o.CustomerID }) + .GroupBy(x => x.Second) + .Select(g => new { Sum = g.Sum(x => x.First), Count = g.Count(x => x.Second.StartsWith("Lon")) }).ToList() + }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + Assert.Equal(e.Key, a.Key); + AssertCollection(e.Subquery, a.Subquery); + }))).Message; + + Assert.Equal(RelationalStrings.MissingIdentifyingProjectionInDistinctGroupBySubquery("o.OrderID"), message); + } + + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Select_nested_collection_with_distinct(bool async) + { + var message = (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set() + .OrderBy(c => c.CustomerID) + .Where(c => c.CustomerID.StartsWith("A")) + .Select( + c => c.Orders.Any() + ? c.Orders.Select(o => o.CustomerID).Distinct().ToArray() + : Array.Empty()), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)))).Message; + + Assert.Equal(RelationalStrings.MissingIdentifyingProjectionInDistinctGroupBySubquery("o.OrderID"), message); + } + protected virtual bool CanExecuteQueryString => false; diff --git a/test/EFCore.Relational.Specification.Tests/Query/NorthwindKeylessEntitiesQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/NorthwindKeylessEntitiesQueryRelationalTestBase.cs index 0a3403b922c..ff87c91a4f0 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/NorthwindKeylessEntitiesQueryRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/NorthwindKeylessEntitiesQueryRelationalTestBase.cs @@ -37,7 +37,23 @@ public virtual async Task Projecting_collection_correlated_with_keyless_entity_t OrderDetailIds = ss.Set().Where(c => c.City == cq.City).ToList() }).OrderBy(x => x.City).Take(2)))).Message; - Assert.Equal(RelationalStrings.ProjectingCollectionOnKeylessEntityNotSupported, message); + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Collection_of_entities_projecting_correlated_collection_of_keyless_entities(bool async) + { + var message = (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set().OrderBy(c => c.CustomerID).Select(c => new + { + c.City, + Collection = ss.Set().Where(cq => cq.City == c.City).ToList(), + })))).Message; + + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); } protected override QueryAsserter CreateQueryAsserter(TFixture fixture) diff --git a/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs index 5f7ba263335..16cdbb3122c 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs @@ -1409,17 +1409,22 @@ public virtual void Scalar_Nested_Function_UDF_BCL_Instance() #region TableValuedFunction - [ConditionalFact(Skip = "Issue#15873")] + [ConditionalFact] public virtual void QF_Anonymous_Collection_No_PK_Throws() { using (var context = CreateContext()) { var query = from c in context.Customers - select new { c.Id, products = context.GetTopSellingProductsForCustomer(c.Id).ToList() }; + select new + { + c.Id, + products = context.GetTopSellingProductsForCustomer(c.Id).ToList(), + orders = context.Orders.Where(o => o.CustomerId == c.Id).ToList() + }; - //Assert.Contains( - // RelationalStrings.DbFunctionProjectedCollectionMustHavePK("GetTopSellingProductsForCustomer"), - // Assert.Throws(() => query.ToList()).Message); + Assert.Equal( + RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, + Assert.Throws(() => query.ToList()).Message); } } @@ -1526,22 +1531,20 @@ from r in context.GetCustomerOrderCountByYear(c.Id) } } - [ConditionalFact(Skip = "Issue#20184")] + [ConditionalFact] public virtual void QF_Select_Direct_In_Anonymous() { using (var context = CreateContext()) { - var results = (from c in context.Customers - select new - { - c.Id, Prods = context.GetTopTwoSellingProducts().ToList(), - }).ToList(); - - Assert.Equal(4, results.Count); - Assert.Equal(2, results[0].Prods.Count); - Assert.Equal(2, results[1].Prods.Count); - Assert.Equal(2, results[2].Prods.Count); - Assert.Equal(2, results[3].Prods.Count); + var message = Assert.Throws( + () => (from c in context.Customers + select new + { + c.Id, + Prods = context.GetTopTwoSellingProducts().ToList(), + }).ToList()).Message; + + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); } } @@ -1618,36 +1621,24 @@ select a.OrderId } } - [ConditionalFact(Skip = "Issue#20184")] + [ConditionalFact] public virtual void QF_Select_Correlated_Subquery_In_Anonymous_Nested() { using (var context = CreateContext()) { - var results = (from c in context.Customers - select new + var message = Assert.Throws( + () => (from c in context.Customers + select new + { + c.Id, + OrderCountYear = context.GetOrdersWithMultipleProducts(c.Id).Where(o => o.OrderDate.Day == 21).Select(o => new { - c.Id, - OrderCountYear = context.GetOrdersWithMultipleProducts(c.Id).Where(o => o.OrderDate.Day == 21).Select( - o => new - { - OrderCountYearNested = context.GetOrdersWithMultipleProducts(o.CustomerId).ToList(), - Prods = context.GetTopTwoSellingProducts().ToList(), - }).ToList() - }).ToList(); - - Assert.Equal(4, results.Count); - - Assert.Single(results[0].OrderCountYear); - Assert.Equal(2, results[0].OrderCountYear[0].Prods.Count); - Assert.Equal(2, results[0].OrderCountYear[0].OrderCountYearNested.Count); + OrderCountYearNested = context.GetOrdersWithMultipleProducts(o.CustomerId).ToList(), + Prods = context.GetTopTwoSellingProducts().ToList(), + }).ToList() + }).ToList()).Message; - Assert.Single(results[1].OrderCountYear); - Assert.Equal(2, results[1].OrderCountYear[0].Prods.Count); - Assert.Equal(2, results[1].OrderCountYear[0].OrderCountYearNested.Count); - - Assert.Empty(results[2].OrderCountYear); - - Assert.Empty(results[3].OrderCountYear); + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); } } @@ -1656,25 +1647,16 @@ public virtual void QF_Select_Correlated_Subquery_In_Anonymous_MultipleCollectio { using (var context = CreateContext()) { - var results = (from c in context.Customers - select new - { - c.Id, - Prods = context.GetTopTwoSellingProducts().Where(p => p.AmountSold == 249).Select(p => p.ProductId) - .ToList(), - Addresses = c.Addresses.Where(a => a.State == "NY").ToList() - }).ToList(); - - Assert.Equal(4, results.Count); - Assert.Equal(3, results[0].Prods[0]); - Assert.Equal(3, results[1].Prods[0]); - Assert.Equal(3, results[2].Prods[0]); - Assert.Equal(3, results[3].Prods[0]); - - Assert.Empty(results[0].Addresses); - Assert.Equal("Apartment 5A, 129 West 81st Street", results[1].Addresses[0].Street); - Assert.Equal("425 Grove Street, Apt 20", results[2].Addresses[0].Street); - Assert.Empty(results[3].Addresses); + var message = Assert.Throws( + () => (from c in context.Customers + select new + { + c.Id, + Addresses = c.Addresses.Where(a => a.State == "NY").ToList(), + Prods = context.GetTopTwoSellingProducts().Where(p => p.AmountSold == 249).Select(p => p.ProductId).ToList() + }).ToList()).Message; + + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); } } @@ -1683,19 +1665,15 @@ public virtual void QF_Select_NonCorrelated_Subquery_In_Anonymous() { using (var context = CreateContext()) { - var results = (from c in context.Customers - select new - { - c.Id, - Prods = context.GetTopTwoSellingProducts().Where(p => p.AmountSold == 249).Select(p => p.ProductId) - .ToList(), - }).ToList(); - - Assert.Equal(4, results.Count); - Assert.Equal(3, results[0].Prods[0]); - Assert.Equal(3, results[1].Prods[0]); - Assert.Equal(3, results[2].Prods[0]); - Assert.Equal(3, results[3].Prods[0]); + var message = Assert.Throws( + () => (from c in context.Customers + select new + { + c.Id, + Prods = context.GetTopTwoSellingProducts().Select(p => p.ProductId).ToList(), + }).ToList()).Message; + + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); } } @@ -1705,20 +1683,15 @@ public virtual void QF_Select_NonCorrelated_Subquery_In_Anonymous_Parameter() using (var context = CreateContext()) { var amount = 27; - - var results = (from c in context.Customers - select new - { - c.Id, - Prods = context.GetTopTwoSellingProducts().Where(p => p.AmountSold == amount).Select(p => p.ProductId) - .ToList(), - }).ToList(); - - Assert.Equal(4, results.Count); - Assert.Single(results[0].Prods); - Assert.Single(results[1].Prods); - Assert.Single(results[2].Prods); - Assert.Single(results[3].Prods); + var message = Assert.Throws( + () => (from c in context.Customers + select new + { + c.Id, + Prods = context.GetTopTwoSellingProducts().Where(p => p.AmountSold == amount).Select(p => p.ProductId).ToList(), + }).ToList()).Message; + + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); } } diff --git a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs index d34e79efc46..bd6e8865495 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs @@ -1998,7 +1998,7 @@ public virtual Task Distinct_GroupBy_OrderBy_key(bool async) assertOrder: true); } - [ConditionalTheory(Skip = "Issue #15873")] + [ConditionalTheory(Skip = "Issue #21965")] [MemberData(nameof(IsAsyncData))] public virtual Task Select_nested_collection_with_groupby(bool async) { @@ -2011,6 +2011,42 @@ public virtual Task Select_nested_collection_with_groupby(bool async) : Array.Empty())); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Select_uncorrelated_collection_with_groupby_works(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .OrderBy(c => c.CustomerID) + .Where(c => c.CustomerID.StartsWith("A")) + .Select(c => ss.Set().GroupBy(o => o.OrderID).Select(g => g.Key).ToArray()), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Select_uncorrelated_collection_with_groupby_multiple_collections_work(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(c => c.CustomerID.StartsWith("A")) + .Select(c => c.Customer.City) + .Select(c => new + { + c1 = ss.Set().GroupBy(p => p.ProductID).Select(g => g.Key).ToArray(), + c2 = ss.Set().GroupBy(p => p.ProductID).Select(g => g.Count()).ToArray() + }), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertCollection(e.c1, a.c1); + AssertCollection(e.c2, a.c2); + }); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Select_GroupBy_All(bool async) @@ -2894,31 +2930,6 @@ public virtual Task Complex_query_with_groupBy_in_subquery3(bool async) }); } - // also 15279 - [ConditionalTheory(Skip = "issue #15873")] - [MemberData(nameof(IsAsyncData))] - public virtual Task Complex_query_with_groupBy_in_subquery4(bool async) - { - return AssertQuery( - async, - ss => ss.Set() - .Select( - c => new - { - Key = c.CustomerID, - Subquery = c.Orders - .Select(o => new { First = o.OrderID, Second = o.Customer.City + o.CustomerID }) - .GroupBy(x => x.Second) - .Select(g => new { Sum = g.Sum(x => x.First), Count = g.Count(x => x.Second.StartsWith("Lon")) }).ToList() - }), - elementSorter: e => e.Key, - elementAsserter: (e, a) => - { - Assert.Equal(e.Key, a.Key); - AssertCollection(e.Subquery, a.Subquery); - }); - } - [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_scalar_subquery(bool async) diff --git a/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs index 38e5dc9832c..ba42249e117 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs @@ -1795,7 +1795,7 @@ public virtual Task Projecting_multiple_collection_with_same_constant_works(bool [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Projecting_after_navigation_and_distinct_works_correctly(bool async) + public virtual Task Projecting_after_navigation_and_distinct_throws(bool async) { var filteredOrderIds = new[] { 10248, 10249, 10250 }; diff --git a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs index 50b0b35bbc3..f6c445363f2 100644 --- a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs @@ -858,6 +858,25 @@ public virtual async Task Query_on_collection_entry_works_for_owned_collection(b } } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Projecting_collection_correlated_with_keyless_entity_after_navigation_works_using_parent_identifiers(bool async) + { + return AssertQuery( + async, + ss => ss.Set().OrderBy(f => f.Id).Select(f => f.Barton.Throned.Value).Select(t => new + { + t, + Planets = ss.Set().Where(p => p.Id != t).ToList() + }), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertEqual(e.t, a.t); + AssertCollection(e.Planets, a.Planets); + }); + } + protected virtual DbContext CreateContext() => Fixture.CreateContext(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs index 3182abda89b..efeb76d3d0b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs @@ -1,7 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Xunit; using Xunit.Abstractions; namespace Microsoft.EntityFrameworkCore.Query @@ -163,6 +166,14 @@ WHERE [l2].[OneToMany_Required_Inverse3Id] IS NOT NULL AND [l2].[Level2_Required ORDER BY [l].[Id], [t].[Id], [t].[Id0], [t1].[Id], [t1].[Id0], [t1].[Id00]"); } + public override async Task SelectMany_with_navigation_and_Distinct(bool async) + { + var message = (await Assert.ThrowsAsync( + () => base.SelectMany_with_navigation_and_Distinct(async))).Message; + + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs index 884336091dd..bc95c705944 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs @@ -1511,6 +1511,43 @@ FROM [Orders] AS [o1] ORDER BY [o1].[OrderID]"); } + public override async Task Select_uncorrelated_collection_with_groupby_works(bool async) + { + await base.Select_uncorrelated_collection_with_groupby_works(async); + + AssertSql( + @"SELECT [c].[CustomerID], [t].[OrderID] +FROM [Customers] AS [c] +OUTER APPLY ( + SELECT [o].[OrderID] + FROM [Orders] AS [o] + GROUP BY [o].[OrderID] +) AS [t] +WHERE [c].[CustomerID] LIKE N'A%' +ORDER BY [c].[CustomerID], [t].[OrderID]"); + } + + public override async Task Select_uncorrelated_collection_with_groupby_multiple_collections_work(bool async) + { + await base.Select_uncorrelated_collection_with_groupby_multiple_collections_work(async); + + AssertSql( + @"SELECT [o].[OrderID], [t].[ProductID], [t0].[c], [t0].[ProductID] +FROM [Orders] AS [o] +OUTER APPLY ( + SELECT [p].[ProductID] + FROM [Products] AS [p] + GROUP BY [p].[ProductID] +) AS [t] +OUTER APPLY ( + SELECT COUNT(*) AS [c], [p0].[ProductID] + FROM [Products] AS [p0] + GROUP BY [p0].[ProductID] +) AS [t0] +WHERE [o].[CustomerID] IS NOT NULL AND ([o].[CustomerID] LIKE N'A%') +ORDER BY [o].[OrderID], [t].[ProductID], [t0].[ProductID]"); + } + public override async Task Select_GroupBy_All(bool async) { await base.Select_GroupBy_All(async); @@ -2399,14 +2436,6 @@ FROM [Orders] AS [o] GROUP BY [o].[OrderID]"); } - public override async Task Complex_query_with_groupBy_in_subquery4(bool async) - { - await base.Complex_query_with_groupBy_in_subquery4(async); - - AssertSql( - @""); - } - public override async Task Group_by_with_arithmetic_operation_inside_aggregate(bool async) { await base.Group_by_with_arithmetic_operation_inside_aggregate(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs index bc838c81d61..5333925e3bd 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -5273,6 +5273,33 @@ WHERE [c].[CustomerID] LIKE N'F%' ORDER BY [c].[CustomerID]"); } + public override async Task SelectMany_correlated_subquery_hard(bool async) + { + await base.SelectMany_correlated_subquery_hard(async); + + AssertSql( + @"@__p_0='91' + +SELECT [t0].[City] AS [c1], [t1].[City], [t1].[City0] AS [c1] +FROM ( + SELECT DISTINCT [t].[City] + FROM ( + SELECT TOP(@__p_0) [c].[City] + FROM [Customers] AS [c] + ) AS [t] +) AS [t0] +CROSS APPLY ( + SELECT TOP(9) [e].[City], [t0].[City] AS [City0] + FROM [Employees] AS [e] + WHERE ([t0].[City] = [e].[City]) OR ([t0].[City] IS NULL AND [e].[City] IS NULL) +) AS [t1] +CROSS APPLY ( + SELECT TOP(9) [t0].[City], [e0].[EmployeeID] + FROM [Employees] AS [e0] + WHERE ([t1].[City] = [e0].[City]) OR ([t1].[City] IS NULL AND [e0].[City] IS NULL) +) AS [t2]"); + } + 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 863f4a29458..1c6e930a069 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs @@ -1,9 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit; using Xunit.Abstractions; namespace Microsoft.EntityFrameworkCore.Query @@ -1424,23 +1426,12 @@ FROM [Customers] AS [c] ORDER BY [c].[CustomerID], [o].[OrderID], [o0].[OrderID]"); } - public override async Task Projecting_after_navigation_and_distinct_works_correctly(bool async) + public override async Task Projecting_after_navigation_and_distinct_throws(bool async) { - await base.Projecting_after_navigation_and_distinct_works_correctly(async); + var message = (await Assert.ThrowsAsync( + () => base.Projecting_after_navigation_and_distinct_throws(async))).Message; - AssertSql( - @"SELECT [t].[CustomerID], [t0].[CustomerID], [t0].[OrderID], [t0].[OrderDate] -FROM ( - SELECT DISTINCT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] - FROM [Orders] AS [o] - LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] -) AS [t] -OUTER APPLY ( - SELECT [t].[CustomerID], [o0].[OrderID], [o0].[OrderDate] - FROM [Orders] AS [o0] - WHERE [o0].[OrderID] IN (10248, 10249, 10250) AND (([t].[CustomerID] = [o0].[CustomerID]) OR ([t].[CustomerID] IS NULL AND [o0].[CustomerID] IS NULL)) -) AS [t0] -ORDER BY [t].[CustomerID], [t0].[OrderID]"); + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); } public override Task Reverse_without_explicit_ordering_throws(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs index 00ceeab5988..f2fbb73d85f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs @@ -994,6 +994,18 @@ WHERE [o6].[LeafAAddress_LeafType] IS NOT NULL ORDER BY [o].[Id], [t].[Id], [t].[Id0], [t1].[Id], [t1].[Id0], [t3].[Id], [t3].[Id0], [t5].[Id], [t5].[Id0], [o8].[ClientId], [o8].[Id]"); } + public override async Task Projecting_collection_correlated_with_keyless_entity_after_navigation_works_using_parent_identifiers(bool async) + { + await base.Projecting_collection_correlated_with_keyless_entity_after_navigation_works_using_parent_identifiers(async); + + AssertSql( + @"SELECT [b].[Throned_Value], [f].[Id], [b].[Id], [p].[Id], [p].[StarId] +FROM [Fink] AS [f] +LEFT JOIN [Barton] AS [b] ON [f].[BartonId] = [b].[Id] +LEFT JOIN [Planet] AS [p] ON ([b].[Throned_Value] <> [p].[Id]) OR [b].[Throned_Value] IS NULL +ORDER BY [f].[Id], [b].[Id], [p].[Id]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs index 86a2ff9feff..7dd0a3978a8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs @@ -533,17 +533,6 @@ CROSS APPLY [dbo].[GetCustomerOrderCountByYear]([c].[Id]) AS [g] ORDER BY [g].[Year]"); } - public override void QF_Select_Direct_In_Anonymous() - { - base.QF_Select_Direct_In_Anonymous(); - - AssertSql( - @"SELECT [t].[AmountSold], [t].[ProductId] -FROM [dbo].[GetTopTwoSellingProducts]() AS [t]", - @"SELECT [c].[Id] -FROM [Customers] AS [c]"); - } - public override void QF_Select_Correlated_Direct_With_Function_Query_Parameter_Correlated_In_Anonymous() { base.QF_Select_Correlated_Direct_With_Function_Query_Parameter_Correlated_In_Anonymous(); @@ -585,76 +574,6 @@ CROSS APPLY [dbo].[GetOrdersWithMultipleProducts]([c].[Id]) AS [g] ) AS [t] ON [o].[Id] = [t].[OrderId]"); } - public override void QF_Select_Correlated_Subquery_In_Anonymous_Nested() - { - base.QF_Select_Correlated_Subquery_In_Anonymous_Nested(); - - AssertSql( - @"SELECT [t].[AmountSold], [t].[ProductId] -FROM [dbo].[GetTopTwoSellingProducts]() AS [t]", - @"SELECT [c].[Id], [t].[OrderId], [t].[OrderId0], [t].[CustomerId], [t].[OrderDate] -FROM [Customers] AS [c] -OUTER APPLY ( - SELECT [m].[OrderId], [m0].[OrderId] AS [OrderId0], [m0].[CustomerId], [m0].[OrderDate] - FROM [dbo].[GetOrdersWithMultipleProducts]([c].[Id]) AS [m] - OUTER APPLY [dbo].[GetOrdersWithMultipleProducts]([m].[CustomerId]) AS [m0] - WHERE DATEPART(day, [m].[OrderDate]) = 21 -) AS [t] -ORDER BY [c].[Id], [t].[OrderId], [t].[OrderId0]"); - } - - public override void QF_Select_Correlated_Subquery_In_Anonymous_MultipleCollections() - { - base.QF_Select_Correlated_Subquery_In_Anonymous_MultipleCollections(); - - AssertSql( - @"SELECT [c].[Id], [t].[ProductId], [t0].[Id], [t0].[City], [t0].[CustomerId], [t0].[State], [t0].[Street] -FROM [Customers] AS [c] -OUTER APPLY ( - SELECT [g].[ProductId] - FROM [dbo].[GetTopTwoSellingProducts]() AS [g] - WHERE [g].[AmountSold] = 249 -) AS [t] -LEFT JOIN ( - SELECT [a].[Id], [a].[City], [a].[CustomerId], [a].[State], [a].[Street] - FROM [Addresses] AS [a] - WHERE [a].[State] = N'NY' -) AS [t0] ON [c].[Id] = [t0].[CustomerId] -ORDER BY [c].[Id], [t0].[Id]"); - } - - public override void QF_Select_NonCorrelated_Subquery_In_Anonymous() - { - base.QF_Select_NonCorrelated_Subquery_In_Anonymous(); - - AssertSql( - @"SELECT [c].[Id], [t].[ProductId] -FROM [Customers] AS [c] -OUTER APPLY ( - SELECT [g].[ProductId] - FROM [dbo].[GetTopTwoSellingProducts]() AS [g] - WHERE [g].[AmountSold] = 249 -) AS [t] -ORDER BY [c].[Id]"); - } - - public override void QF_Select_NonCorrelated_Subquery_In_Anonymous_Parameter() - { - base.QF_Select_NonCorrelated_Subquery_In_Anonymous_Parameter(); - - AssertSql( - @"@__amount_1='27' (Nullable = true) - -SELECT [c].[Id], [t].[ProductId] -FROM [Customers] AS [c] -OUTER APPLY ( - SELECT [g].[ProductId] - FROM [dbo].[GetTopTwoSellingProducts]() AS [g] - WHERE [g].[AmountSold] = @__amount_1 -) AS [t] -ORDER BY [c].[Id]"); - } - public override void QF_Correlated_Select_In_Anonymous() { base.QF_Correlated_Select_In_Anonymous(); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsWeakQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsWeakQuerySqliteTest.cs index 17181016092..692ae597743 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsWeakQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsWeakQuerySqliteTest.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Sqlite.Internal; using Xunit; using Xunit.Abstractions; @@ -17,6 +18,14 @@ public ComplexNavigationsWeakQuerySqliteTest(ComplexNavigationsWeakQuerySqliteFi { } + public override async Task SelectMany_with_navigation_and_Distinct(bool async) + { + var message = (await Assert.ThrowsAsync( + () => base.SelectMany_with_navigation_and_Distinct(async))).Message; + + Assert.Equal(RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, message); + } + public override async Task Filtered_include_after_different_filtered_include_different_level(bool async) => Assert.Equal( SqliteStrings.ApplyNotSupported, diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindGroupByQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindGroupByQuerySqliteTest.cs index 65a9dd1266c..a8e5c490d60 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindGroupByQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindGroupByQuerySqliteTest.cs @@ -1,7 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Sqlite.Internal; using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit; using Xunit.Abstractions; namespace Microsoft.EntityFrameworkCore.Query @@ -15,5 +19,17 @@ public NorthwindGroupByQuerySqliteTest(NorthwindQuerySqliteFixture Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Select_uncorrelated_collection_with_groupby_multiple_collections_work(async))).Message); + + public override async Task Select_uncorrelated_collection_with_groupby_works(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Select_uncorrelated_collection_with_groupby_works(async))).Message); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSelectQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSelectQuerySqliteTest.cs index 13ec6ed88b8..4a4cb3d04b3 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSelectQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSelectQuerySqliteTest.cs @@ -186,11 +186,11 @@ public override async Task SelectMany_whose_selector_references_outer_source(boo (await Assert.ThrowsAsync( () => base.SelectMany_whose_selector_references_outer_source(async))).Message); - public override async Task Projecting_after_navigation_and_distinct_works_correctly(bool async) + public override async Task Projecting_after_navigation_and_distinct_throws(bool async) => Assert.Equal( - SqliteStrings.ApplyNotSupported, + RelationalStrings.InsufficientInformationToIdentifyOuterElementOfCollectionJoin, (await Assert.ThrowsAsync( - () => base.Projecting_after_navigation_and_distinct_works_correctly(async))).Message); + () => base.Projecting_after_navigation_and_distinct_throws(async))).Message); public override async Task Select_nested_collection_deep(bool async) => Assert.Equal(