diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs index b8e45e9732c..a516113de63 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs @@ -28,6 +28,9 @@ public class SqlServerQueryableMethodTranslatingExpressionVisitor : RelationalQu private static readonly bool UseOldBehavior32374 = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32374", out var enabled32374) && enabled32374; + private static readonly bool UseOldBehavior33932 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue33932", out var enabled33932) && enabled33932; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -446,8 +449,9 @@ static IEnumerable GetAllNavigationsInHierarchy(IEntityType entityT Offset: null, Orderings: [], Projection: [{ Expression: ColumnExpression { Name: "value", Table: var projectionColumnTable } }] - } + } subquery } + && (UseOldBehavior33932 || subquery.Predicate is null) && projectionColumnTable == openJsonExpression) { var newInExpression = _sqlExpressionFactory.In(translatedItem, parameter); @@ -493,6 +497,7 @@ static IEnumerable GetAllNavigationsInHierarchy(IEntityType entityT ] } selectExpression && TranslateExpression(index) is { } translatedIndex + && (UseOldBehavior33932 || selectExpression.Predicate is null) && orderingTable == openJsonExpression) { // Index on JSON array diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs index d9830f7a77c..b68bbb2a0a0 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs @@ -21,6 +21,9 @@ public class SqliteQueryableMethodTranslatingExpressionVisitor : RelationalQuery private readonly SqliteSqlExpressionFactory _sqlExpressionFactory; private readonly bool _areJsonFunctionsSupported; + private static readonly bool UseOldBehavior33932 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue33932", out var enabled33932) && enabled33932; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -82,7 +85,8 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis IsDistinct: false, Limit: null, Offset: null - }) + } subquery + && (UseOldBehavior33932 || subquery.Predicate is null)) { var translation = _sqlExpressionFactory.GreaterThan( @@ -180,7 +184,8 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis IsDistinct: false, Limit: null, Offset: null - }) + } subquery + && (UseOldBehavior33932 || subquery.Predicate is null)) { var translation = _sqlExpressionFactory.Function( "json_array_length", @@ -456,7 +461,8 @@ static IEnumerable GetAllNavigationsInHierarchy(IEntityType entityT Offset: null } selectExpression && orderingColumn.Table == jsonEachExpression - && TranslateExpression(index) is { } translatedIndex) + && TranslateExpression(index) is { } translatedIndex + && (UseOldBehavior33932 || selectExpression.Predicate is null)) { // Index on JSON array diff --git a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs index 52fefce0142..82f8ce725ad 100644 --- a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs @@ -386,6 +386,20 @@ public virtual Task Column_collection_Length(bool async) async, ss => ss.Set().Where(c => c.Ints.Length == 2)); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Column_collection_Count_with_predicate(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(c => c.Ints.Count(i => i > 1) == 2)); + + [ConditionalTheory] // #33932 + [MemberData(nameof(IsAsyncData))] + public virtual Task Column_collection_Where_Count(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(c => c.Ints.Where(i => i > 1).Count() == 2)); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Column_collection_index_int(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs index 19ae6b3d320..222b2e9f8c4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs @@ -468,6 +468,12 @@ public override Task Column_collection_Count_method(bool async) public override Task Column_collection_Length(bool async) => AssertCompatibilityLevelTooLow(() => base.Column_collection_Length(async)); + public override Task Column_collection_Count_with_predicate(bool async) + => AssertCompatibilityLevelTooLow(() => base.Column_collection_Count_with_predicate(async)); + + public override Task Column_collection_Where_Count(bool async) + => AssertCompatibilityLevelTooLow(() => base.Column_collection_Where_Count(async)); + public override Task Column_collection_index_int(bool async) => AssertCompatibilityLevelTooLow(() => base.Column_collection_index_int(async)); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs index 57dd49731ae..81211150f9b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs @@ -663,6 +663,36 @@ FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i]) = 2 """); } + public override async Task Column_collection_Count_with_predicate(bool async) + { + await base.Column_collection_Count_with_predicate(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i] + WHERE [i].[value] > 1) = 2 +"""); + } + + public override async Task Column_collection_Where_Count(bool async) + { + await base.Column_collection_Where_Count(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i] + WHERE [i].[value] > 1) = 2 +"""); + } + public override async Task Column_collection_index_int(bool async) { await base.Column_collection_index_int(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs index 12ba1fc3033..301d7906cc0 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs @@ -649,6 +649,37 @@ WHERE json_array_length("p"."Ints") = 2 """); } + public override async Task Column_collection_Count_with_predicate(bool async) + { + await base.Column_collection_Count_with_predicate(async); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE ( + SELECT COUNT(*) + FROM json_each("p"."Ints") AS "i" + WHERE "i"."value" > 1) = 2 +"""); + } + + // #33932 + public override async Task Column_collection_Where_Count(bool async) + { + await base.Column_collection_Where_Count(async); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE ( + SELECT COUNT(*) + FROM json_each("p"."Ints") AS "i" + WHERE "i"."value" > 1) = 2 +"""); + } + public override async Task Column_collection_index_int(bool async) { await base.Column_collection_index_int(async);