From 22906cdff34c60d75bc90a4b04fd56830db08443 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Thu, 16 Sep 2021 18:50:23 +0200 Subject: [PATCH 1/6] Fix Fluent APIs on keys in model snapshot (#26058) Also generate Fluent API for IsMemoryOptimized. Fixes #26045 --- .../Design/CSharpSnapshotGenerator.cs | 63 +++++++------ .../SqlServerAnnotationCodeGenerator.cs | 19 +++- .../Migrations/ModelSnapshotSqlServerTest.cs | 94 +++++++++++++++++++ .../SqlServerAnnotationCodeGeneratorTest.cs | 23 +++++ 4 files changed, 168 insertions(+), 31 deletions(-) diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 1a4354ebf7a..404be88877f 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; @@ -614,28 +615,32 @@ protected virtual void GenerateKey( Check.NotNull(key, nameof(key)); Check.NotNull(stringBuilder, nameof(stringBuilder)); - stringBuilder - .AppendLine() + var keyBuilderName = new StringBuilder() .Append(entityTypeBuilderName) .Append(primary ? ".HasKey(" : ".HasAlternateKey(") .Append(string.Join(", ", key.Properties.Select(p => Code.Literal(p.Name)))) - .Append(")"); + .Append(")") + .ToString(); + + stringBuilder + .AppendLine() + .Append(keyBuilderName); // Note that GenerateAnnotations below does the corresponding decrement stringBuilder.IncrementIndent(); - GenerateKeyAnnotations(entityTypeBuilderName, key, stringBuilder); + GenerateKeyAnnotations(keyBuilderName, key, stringBuilder); } /// /// Generates code for the annotations on a key. /// - /// The name of the builder variable. + /// The name of the builder variable. /// The key. /// The builder code is added to. - protected virtual void GenerateKeyAnnotations(string entityTypeBuilderName, IKey key, IndentedStringBuilder stringBuilder) + protected virtual void GenerateKeyAnnotations(string keyBuilderName, IKey key, IndentedStringBuilder stringBuilder) { - Check.NotNull(entityTypeBuilderName, nameof(entityTypeBuilderName)); + Check.NotNull(keyBuilderName, nameof(keyBuilderName)); Check.NotNull(key, nameof(key)); Check.NotNull(stringBuilder, nameof(stringBuilder)); @@ -643,7 +648,7 @@ protected virtual void GenerateKeyAnnotations(string entityTypeBuilderName, IKey .FilterIgnoredAnnotations(key.GetAnnotations()) .ToDictionary(a => a.Name, a => a); - GenerateAnnotations(entityTypeBuilderName, key, stringBuilder, annotations, inChainedCall: true); + GenerateAnnotations(keyBuilderName, key, stringBuilder, annotations, inChainedCall: true); } /// @@ -1056,9 +1061,11 @@ protected virtual void GenerateForeignKey( Check.NotNull(foreignKey, nameof(foreignKey)); Check.NotNull(stringBuilder, nameof(stringBuilder)); + var foreignKeyBuilderNameStringBuilder = new StringBuilder(); + if (!foreignKey.IsOwnership) { - stringBuilder + foreignKeyBuilderNameStringBuilder .Append(entityTypeBuilderName) .Append(".HasOne(") .Append(Code.Literal(foreignKey.PrincipalEntityType.Name)) @@ -1070,19 +1077,23 @@ protected virtual void GenerateForeignKey( } else { - stringBuilder + foreignKeyBuilderNameStringBuilder .Append(entityTypeBuilderName) .Append(".WithOwner("); if (foreignKey.DependentToPrincipal != null) { - stringBuilder + foreignKeyBuilderNameStringBuilder .Append(Code.Literal(foreignKey.DependentToPrincipal.Name)); } } + foreignKeyBuilderNameStringBuilder.Append(")"); + + var foreignKeyBuilderName = foreignKeyBuilderNameStringBuilder.ToString(); + stringBuilder - .Append(")") + .Append(foreignKeyBuilderName) .AppendLine(); // Note that GenerateAnnotations below does the corresponding decrement @@ -1170,21 +1181,21 @@ protected virtual void GenerateForeignKey( } } - GenerateForeignKeyAnnotations(entityTypeBuilderName, foreignKey, stringBuilder); + GenerateForeignKeyAnnotations(foreignKeyBuilderName, foreignKey, stringBuilder); } /// /// Generates code for the annotations on a foreign key. /// - /// The name of the builder variable. + /// The name of the builder variable. /// The foreign key. /// The builder code is added to. protected virtual void GenerateForeignKeyAnnotations( - string entityTypeBuilderName, + string foreignKeyBuilderName, IForeignKey foreignKey, IndentedStringBuilder stringBuilder) { - Check.NotNull(entityTypeBuilderName, nameof(entityTypeBuilderName)); + Check.NotNull(foreignKeyBuilderName, nameof(foreignKeyBuilderName)); Check.NotNull(foreignKey, nameof(foreignKey)); Check.NotNull(stringBuilder, nameof(stringBuilder)); @@ -1192,7 +1203,7 @@ protected virtual void GenerateForeignKeyAnnotations( .FilterIgnoredAnnotations(foreignKey.GetAnnotations()) .ToDictionary(a => a.Name, a => a); - GenerateAnnotations(entityTypeBuilderName, foreignKey, stringBuilder, annotations, inChainedCall: true); + GenerateAnnotations(foreignKeyBuilderName, foreignKey, stringBuilder, annotations, inChainedCall: true); } /// @@ -1269,11 +1280,9 @@ protected virtual void GenerateNavigation( Check.NotNull(navigation, nameof(navigation)); Check.NotNull(stringBuilder, nameof(stringBuilder)); - stringBuilder - .Append(entityTypeBuilderName) - .Append(".Navigation(") - .Append(Code.Literal(navigation.Name)) - .Append(")"); + var navigationBuilderName = $"{entityTypeBuilderName}.Navigation({Code.Literal(navigation.Name)})"; + + stringBuilder.Append(navigationBuilderName); // Note that GenerateAnnotations below does the corresponding decrement stringBuilder.IncrementIndent(); @@ -1287,21 +1296,21 @@ protected virtual void GenerateNavigation( .Append(".IsRequired()"); } - GenerateNavigationAnnotations(entityTypeBuilderName, navigation, stringBuilder); + GenerateNavigationAnnotations(navigationBuilderName, navigation, stringBuilder); } /// /// Generates code for the annotations on a navigation. /// - /// The name of the builder variable. + /// The name of the builder variable. /// The navigation. /// The builder code is added to. protected virtual void GenerateNavigationAnnotations( - string entityTypeBuilderName, + string navigationBuilderName, INavigation navigation, IndentedStringBuilder stringBuilder) { - Check.NotNull(entityTypeBuilderName, nameof(entityTypeBuilderName)); + Check.NotNull(navigationBuilderName, nameof(navigationBuilderName)); Check.NotNull(navigation, nameof(navigation)); Check.NotNull(stringBuilder, nameof(stringBuilder)); @@ -1309,7 +1318,7 @@ protected virtual void GenerateNavigationAnnotations( .FilterIgnoredAnnotations(navigation.GetAnnotations()) .ToDictionary(a => a.Name, a => a); - GenerateAnnotations(entityTypeBuilderName, navigation, stringBuilder, annotations, inChainedCall: true); + GenerateAnnotations(navigationBuilderName, navigation, stringBuilder, annotations, inChainedCall: true); } /// diff --git a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs index 395e8ca94ea..73c504e6b1a 100644 --- a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs +++ b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs @@ -52,6 +52,10 @@ private static readonly MethodInfo _entityTypeToTableMethodInfo = typeof(RelationalEntityTypeBuilderExtensions).GetRequiredRuntimeMethod( nameof(RelationalEntityTypeBuilderExtensions.ToTable), typeof(EntityTypeBuilder), typeof(string)); + private static readonly MethodInfo _entityTypeIsMemoryOptimizedMethodInfo + = typeof(SqlServerEntityTypeBuilderExtensions).GetRequiredRuntimeMethod( + nameof(SqlServerEntityTypeBuilderExtensions.IsMemoryOptimized), typeof(EntityTypeBuilder), typeof(bool)); + private static readonly MethodInfo _propertyIsSparseMethodInfo = typeof(SqlServerPropertyBuilderExtensions).GetRequiredRuntimeMethod( nameof(SqlServerPropertyBuilderExtensions.IsSparse), typeof(PropertyBuilder), typeof(bool)); @@ -184,7 +188,14 @@ public override IReadOnlyList GenerateFluentApiCalls( IEntityType entityType, IDictionary annotations) { - var result = base.GenerateFluentApiCalls(entityType, annotations); + var fragments = new List(base.GenerateFluentApiCalls(entityType, annotations)); + + if (GetAndRemove(annotations, SqlServerAnnotationNames.MemoryOptimized) is bool isMemoryOptimized) + { + fragments.Add(isMemoryOptimized + ? new(_entityTypeIsMemoryOptimizedMethodInfo) + : new(_entityTypeIsMemoryOptimizedMethodInfo, false)); + } if (annotations.TryGetValue(SqlServerAnnotationNames.IsTemporal, out var isTemporalAnnotation) && isTemporalAnnotation.Value as bool? == true) @@ -250,16 +261,16 @@ public override IReadOnlyList GenerateFluentApiCalls( "ttb", temporalTableBuilderCalls)))); + fragments.Add(toTemporalTableCall); + annotations.Remove(SqlServerAnnotationNames.IsTemporal); annotations.Remove(SqlServerAnnotationNames.TemporalHistoryTableName); annotations.Remove(SqlServerAnnotationNames.TemporalHistoryTableSchema); annotations.Remove(SqlServerAnnotationNames.TemporalPeriodStartPropertyName); annotations.Remove(SqlServerAnnotationNames.TemporalPeriodEndPropertyName); - - return result.Concat(new[] { toTemporalTableCall }).ToList(); } - return result; + return fragments; } /// diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index d57a8ad97f7..b562a7c4046 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -874,6 +874,37 @@ public virtual void EntityType_annotations_are_stored_in_snapshot() }); } + [ConditionalFact] + public virtual void EntityType_Fluent_APIs_are_properly_generated() + { + Test( + builder => + { + builder.Entity().IsMemoryOptimized(); + builder.Ignore(); + }, + AddBoilerPlate( + GetHeading() + + @" + modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd() + .HasColumnType(""int""); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property(""Id""), 1L, 1); + + b.HasKey(""Id""); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey(""Id""), false); + + b.ToTable(""EntityWithOneProperty""); + + SqlServerEntityTypeBuilderExtensions.IsMemoryOptimized(b); + });"), + o => Assert.True(o.GetEntityTypes().Single().IsMemoryOptimized())); + } + [ConditionalFact] public virtual void BaseType_is_stored_in_snapshot() { @@ -3833,6 +3864,35 @@ public virtual void Key_annotations_are_stored_in_snapshot() "AnnotationValue", o.GetEntityTypes().First().GetKeys().Where(k => !k.IsPrimaryKey()).First()["AnnotationName"])); } + [ConditionalFact] + public virtual void Key_Fluent_APIs_are_properly_generated() + { + Test( + builder => + { + builder.Entity().HasKey(t => t.Id).IsClustered(); + builder.Ignore(); + }, + AddBoilerPlate( + GetHeading() + + @" + modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd() + .HasColumnType(""int""); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property(""Id""), 1L, 1); + + b.HasKey(""Id""); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey(""Id"")); + + b.ToTable(""EntityWithOneProperty""); + });"), + o => Assert.True(o.GetEntityTypes().First().GetKeys().Single(k => k.IsPrimaryKey()).IsClustered())); + } + [ConditionalFact] public virtual void Key_name_annotation_is_stored_in_snapshot_as_fluent_api() { @@ -3946,6 +4006,40 @@ public virtual void Index_annotations_are_stored_in_snapshot() o => Assert.Equal("AnnotationValue", o.GetEntityTypes().First().GetIndexes().First()["AnnotationName"])); } + [ConditionalFact] + public virtual void Index_Fluent_APIs_are_properly_generated() + { + Test( + builder => + { + builder.Entity().HasIndex(t => t.AlternateId).IsClustered(); + builder.Ignore(); + }, + AddBoilerPlate( + GetHeading() + + @" + modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd() + .HasColumnType(""int""); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property(""Id""), 1L, 1); + + b.Property(""AlternateId"") + .HasColumnType(""int""); + + b.HasKey(""Id""); + + b.HasIndex(""AlternateId""); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex(""AlternateId"")); + + b.ToTable(""EntityWithTwoProperties""); + });"), + o => Assert.True(o.GetEntityTypes().Single().GetIndexes().Single().IsClustered())); + } + [ConditionalFact] public virtual void Index_isUnique_is_stored_in_snapshot() { diff --git a/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs b/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs index 2e048bee9dd..a9254179ed5 100644 --- a/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs +++ b/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs @@ -323,6 +323,29 @@ public void GenerateFluentApi_IModel_works_with_PerformanceLevel() Assert.Equal("'foo'", Assert.Single(result.Arguments)); } + [ConditionalFact] + public void GenerateFluentApi_IEntityType_works_when_IsMemoryOptimized() + { + var generator = CreateGenerator(); + + var modelBuilder = SqlServerConventionSetBuilder.CreateModelBuilder(); + modelBuilder.Entity( + "Post", + x => + { + x.Property("Id"); + x.IsMemoryOptimized(); + }); + var entityType = (IEntityType)modelBuilder.Model.FindEntityType("Post"); + + var result = generator.GenerateFluentApiCalls(entityType, entityType.GetAnnotations().ToDictionary(a => a.Name, a => a)) + .Single(); + + Assert.Equal(nameof(SqlServerEntityTypeBuilderExtensions.IsMemoryOptimized), result.Method); + + Assert.Equal(0, result.Arguments.Count); + } + private SqlServerAnnotationCodeGenerator CreateGenerator() => new( new AnnotationCodeGeneratorDependencies( From 58c1003087250b3025853e72c8bbfa4a05e93702 Mon Sep 17 00:00:00 2001 From: Maurycy Markowski Date: Wed, 15 Sep 2021 17:42:40 -0700 Subject: [PATCH 2/6] Add regression test for #25315 - Unable to translate collection subquery in projection The issue has already been fixed. Resolves #25315 --- .../Query/GearsOfWarQueryTestBase.cs | 11 +++++++ .../Query/GearsOfWarQuerySqlServerTest.cs | 25 ++++++++++++++++ .../Query/TPTGearsOfWarQuerySqlServerTest.cs | 29 +++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index 5a02d94205d..a6ea1fc153a 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -9118,6 +9118,17 @@ public virtual Task Project_navigation_defined_on_derived_from_entity_with_inher }); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Join_entity_with_itself_grouped_by_key_followed_by_include_skip_take(bool async) + { + return AssertQuery( + async, + ss => (from g1 in ss.Set() + join g2 in ss.Set().Where(x => x.Nickname != "Dom").GroupBy(x => x.HasSoulPatch).Select(g => g.Min(x => x.Nickname.Length)) on g1.Nickname.Length equals g2 + select g1).Include(x => x.Weapons).OrderBy(x => x.Nickname).Skip(0).Take(10)); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Where_equals_method_on_nullable_with_object_overload(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index d7d70fd8348..eae4227f0e4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -8198,6 +8198,31 @@ LEFT JOIN [Gears] AS [g] ON ([l].[DefeatedByNickname] = [g].[Nickname]) AND ([l] LEFT JOIN [LocustHighCommands] AS [l0] ON [l].[HighCommandId] = [l0].[Id]"); } + public override async Task Join_entity_with_itself_grouped_by_key_followed_by_include_skip_take(bool async) + { + await base.Join_entity_with_itself_grouped_by_key_followed_by_include_skip_take(async); + + AssertSql( + @"@__p_0='0' +@__p_1='10' + +SELECT [t0].[Nickname], [t0].[SquadId], [t0].[AssignedCityName], [t0].[CityOfBirthName], [t0].[Discriminator], [t0].[FullName], [t0].[HasSoulPatch], [t0].[LeaderNickname], [t0].[LeaderSquadId], [t0].[Rank], [t0].[HasSoulPatch0], [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] +FROM ( + SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [t].[HasSoulPatch] AS [HasSoulPatch0] + FROM [Gears] AS [g] + INNER JOIN ( + SELECT MIN(CAST(LEN([g0].[Nickname]) AS int)) AS [c], [g0].[HasSoulPatch] + FROM [Gears] AS [g0] + WHERE [g0].[Nickname] <> N'Dom' + GROUP BY [g0].[HasSoulPatch] + ) AS [t] ON CAST(LEN([g].[Nickname]) AS int) = [t].[c] + ORDER BY [g].[Nickname] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t0] +LEFT JOIN [Weapons] AS [w] ON [t0].[FullName] = [w].[OwnerFullName] +ORDER BY [t0].[Nickname], [t0].[SquadId], [t0].[HasSoulPatch0]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index 14dcaaf851e..ffee3efbccc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -9144,6 +9144,35 @@ FROM [Factions] AS [f] LEFT JOIN [LocustHighCommands] AS [l2] ON [l0].[HighCommandId] = [l2].[Id]"); } + + public override async Task Join_entity_with_itself_grouped_by_key_followed_by_include_skip_take(bool async) + { + await base.Join_entity_with_itself_grouped_by_key_followed_by_include_skip_take(async); + + AssertSql( + @"@__p_0='0' +@__p_1='10' + +SELECT [t0].[Nickname], [t0].[SquadId], [t0].[AssignedCityName], [t0].[CityOfBirthName], [t0].[FullName], [t0].[HasSoulPatch], [t0].[LeaderNickname], [t0].[LeaderSquadId], [t0].[Rank], [t0].[Discriminator], [t0].[HasSoulPatch0], [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] +FROM ( + SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE + WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' + END AS [Discriminator], [t].[HasSoulPatch] AS [HasSoulPatch0] + FROM [Gears] AS [g] + LEFT JOIN [Officers] AS [o] ON ([g].[Nickname] = [o].[Nickname]) AND ([g].[SquadId] = [o].[SquadId]) + INNER JOIN ( + SELECT MIN(CAST(LEN([g0].[Nickname]) AS int)) AS [c], [g0].[HasSoulPatch] + FROM [Gears] AS [g0] + WHERE [g0].[Nickname] <> N'Dom' + GROUP BY [g0].[HasSoulPatch] + ) AS [t] ON CAST(LEN([g].[Nickname]) AS int) = [t].[c] + ORDER BY [g].[Nickname] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t0] +LEFT JOIN [Weapons] AS [w] ON [t0].[FullName] = [w].[OwnerFullName] +ORDER BY [t0].[Nickname], [t0].[SquadId], [t0].[HasSoulPatch0]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } From 4a145767fce173422e9d74e7a38e8690696ac126 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Wed, 15 Sep 2021 12:15:44 -0700 Subject: [PATCH 3/6] Query: Throw better error message when final result contains IGrouping Part 1 of #26046 --- .../NavigationExpandingExpressionVisitor.cs | 7 +++++ .../Query/NorthwindGroupByQueryTestBase.cs | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 2644ef6ffc0..a60786ee949 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -110,6 +110,13 @@ public NavigationExpandingExpressionVisitor( public virtual Expression Expand(Expression query) { var result = Visit(query); + + if (result is GroupByNavigationExpansionExpression) + { + // This indicates that GroupBy was not condensed out of grouping operator. + throw new InvalidOperationException(CoreStrings.TranslationFailed(query.Print())); + } + result = new PendingSelectorExpandingExpressionVisitor(this, _extensibilityHelper, applyIncludes: true).Visit(result); result = Reduce(result); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs index f11d23ba64b..b814c7bd6f5 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs @@ -2750,6 +2750,34 @@ public virtual Task GroupBy_group_Where_Select_Distinct_aggregate(bool async) #region GroupByWithoutAggregate + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_as_final_operator(bool async) + { + return AssertTranslationFailed(() => AssertQuery( + async, + ss => ss.Set().GroupBy(c => c.City))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_Where_with_grouping_result(bool async) + { + return AssertTranslationFailed(() => AssertQuery( + async, + ss => ss.Set().GroupBy(c => c.City).Where(e => e.Key.StartsWith("s")))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_OrderBy_with_grouping_result(bool async) + { + return AssertTranslationFailed(() => AssertQuery( + async, + ss => ss.Set().GroupBy(c => c.City).OrderBy(e => e.Key), + assertOrder: true)); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_SelectMany(bool async) From 45d0ce15727b31dabdceb4d17e22e29ee64c5bbb Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Wed, 15 Sep 2021 14:34:56 -0700 Subject: [PATCH 4/6] Query: Translate ToList over grouping element Part 2 & 3 of #26046 Resolves #26046 --- ...eryExpressionReplacingExpressionVisitor.cs | 51 ++++++++++ ...yableMethodTranslatingExpressionVisitor.cs | 50 +--------- .../Query/SqlExpressions/SelectExpression.cs | 92 +++++++++++++++--- ...ingExpressionVisitor.ExpressionVisitors.cs | 4 +- .../Query/NorthwindGroupByQueryTestBase.cs | 96 ++++++++++++++++--- ...avigationsCollectionsQuerySqlServerTest.cs | 16 ++-- ...tionsCollectionsSplitQuerySqlServerTest.cs | 20 ++-- .../ComplexNavigationsQuerySqlServerTest.cs | 2 +- .../Query/GearsOfWarQuerySqlServerTest.cs | 2 +- .../NorthwindGroupByQuerySqlServerTest.cs | 70 +++++++++++++- .../NorthwindIncludeQuerySqlServerTest.cs | 32 +++---- ...NorthwindSplitIncludeQuerySqlServerTest.cs | 64 ++++++------- .../Query/TPTGearsOfWarQuerySqlServerTest.cs | 2 +- 13 files changed, 357 insertions(+), 144 deletions(-) create mode 100644 src/EFCore.Relational/Query/Internal/QueryExpressionReplacingExpressionVisitor.cs diff --git a/src/EFCore.Relational/Query/Internal/QueryExpressionReplacingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/QueryExpressionReplacingExpressionVisitor.cs new file mode 100644 index 00000000000..97154a73b72 --- /dev/null +++ b/src/EFCore.Relational/Query/Internal/QueryExpressionReplacingExpressionVisitor.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; + +namespace Microsoft.EntityFrameworkCore.Query.Internal +{ + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public class QueryExpressionReplacingExpressionVisitor : ExpressionVisitor + { + private readonly Expression _oldQuery; + private readonly Expression _newQuery; + + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public QueryExpressionReplacingExpressionVisitor(Expression oldQuery, Expression newQuery) + { + _oldQuery = oldQuery; + _newQuery = newQuery; + } + + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [return: NotNullIfNotNull("expression")] + public override Expression? Visit(Expression? expression) + { + return expression is ProjectionBindingExpression projectionBindingExpression + && ReferenceEquals(projectionBindingExpression.QueryExpression, _oldQuery) + ? projectionBindingExpression.ProjectionMember != null + ? new ProjectionBindingExpression( + _newQuery, projectionBindingExpression.ProjectionMember!, projectionBindingExpression.Type) + : new ProjectionBindingExpression( + _newQuery, projectionBindingExpression.Index!.Value, projectionBindingExpression.Type) + : base.Visit(expression); + } + } +} diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index a690b2a13c8..a86be13667b 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -464,29 +463,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent source = TranslateSelect(source, elementSelector); } - if (translatedKey is NewExpression newExpression - && newExpression.Arguments.Count == 0) - { - selectExpression.ApplyGrouping(_sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(1))); - } - else - { - translatedKey = selectExpression.ApplyGrouping(translatedKey); - } - var clonedSelectExpression = selectExpression.Clone(); - // If the grouping key is empty then there may not be any group by terms. - var correlationPredicate = selectExpression.GroupBy.Zip(clonedSelectExpression.GroupBy) - .Select(e => _sqlExpressionFactory.Equal(e.First, e.Second)) - .Aggregate((l, r) => _sqlExpressionFactory.AndAlso(l, r)); - clonedSelectExpression.ClearGroupBy(); - clonedSelectExpression.ApplyPredicate(correlationPredicate); - - var groupByShaper = new GroupByShaperExpression( - translatedKey, - new ShapedQueryExpression( - clonedSelectExpression, - new QueryExpressionReplacingExpressionVisitor(selectExpression, clonedSelectExpression).Visit(source.ShaperExpression))); - + var groupByShaper = selectExpression.ApplyGrouping(translatedKey, source.ShaperExpression, _sqlExpressionFactory); if (resultSelector == null) { return source.UpdateShaperExpression(groupByShaper); @@ -1697,30 +1674,5 @@ static void PopulatePredicateTerms(SqlExpression predicate, List } } } - - private sealed class QueryExpressionReplacingExpressionVisitor : ExpressionVisitor - { - private readonly Expression _oldQuery; - private readonly Expression _newQuery; - - public QueryExpressionReplacingExpressionVisitor(Expression oldQuery, Expression newQuery) - { - _oldQuery = oldQuery; - _newQuery = newQuery; - } - - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - { - return expression is ProjectionBindingExpression projectionBindingExpression - && ReferenceEquals(projectionBindingExpression.QueryExpression, _oldQuery) - ? projectionBindingExpression.ProjectionMember != null - ? new ProjectionBindingExpression( - _newQuery, projectionBindingExpression.ProjectionMember!, projectionBindingExpression.Type) - : new ProjectionBindingExpression( - _newQuery, projectionBindingExpression.Index!.Value, projectionBindingExpression.Type) - : base.Visit(expression); - } - } } } diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 676ef9ce4fb..69eb170f938 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions @@ -1126,7 +1127,7 @@ public void ApplyPredicate(SqlExpression sqlExpression) /// Applies grouping from given key selector. /// /// An key selector expression for the GROUP BY. - public Expression ApplyGrouping(Expression keySelector) + public void ApplyGrouping(Expression keySelector) { Check.NotNull(keySelector, nameof(keySelector)); @@ -1134,7 +1135,7 @@ public Expression ApplyGrouping(Expression keySelector) var groupByTerms = new List(); var groupByAliases = new List(); - AppendGroupBy(keySelector, groupByTerms, groupByAliases, "Key"); + PopulateGroupByTerms(keySelector, groupByTerms, groupByAliases, "Key"); if (groupByTerms.Any(e => e is SqlConstantExpression || e is SqlParameterExpression || e is ScalarSubqueryExpression)) { @@ -1163,19 +1164,86 @@ public Expression ApplyGrouping(Expression keySelector) _identifier.AddRange(_groupBy.Select(e => ((ColumnExpression)e, e.TypeMapping!.KeyComparer))); } } - - return keySelector; } /// - /// Clears existing group by terms. + /// Applies grouping from given key selector and generate to shape results. /// - public void ClearGroupBy() + /// An key selector expression for the GROUP BY. + /// The shaper expression for current query. + /// The sql expression factory to use. + /// A which represents the result of the grouping operation. + public GroupByShaperExpression ApplyGrouping(Expression keySelector, Expression shaperExpression, ISqlExpressionFactory sqlExpressionFactory) { - _groupBy.Clear(); + Check.NotNull(keySelector, nameof(keySelector)); + Check.NotNull(shaperExpression, nameof(shaperExpression)); + Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory)); + + ClearOrdering(); + + var keySelectorToAdd = keySelector; + var emptyKey = keySelector is NewExpression newExpression + && newExpression.Arguments.Count == 0; + if (emptyKey) + { + keySelectorToAdd = sqlExpressionFactory.ApplyDefaultTypeMapping(sqlExpressionFactory.Constant(1)); + } + + var groupByTerms = new List(); + var groupByAliases = new List(); + PopulateGroupByTerms(keySelectorToAdd, groupByTerms, groupByAliases, "Key"); + + if (groupByTerms.Any(e => e is SqlConstantExpression || e is SqlParameterExpression || e is ScalarSubqueryExpression)) + { + // emptyKey will always hit this path. + var sqlRemappingVisitor = PushdownIntoSubqueryInternal(); + var newGroupByTerms = new List(groupByTerms.Count); + var subquery = (SelectExpression)_tables[0]; + var subqueryTableReference = _tableReferences[0]; + for (var i = 0; i < groupByTerms.Count; i++) + { + var item = groupByTerms[i]; + var newItem = subquery._projection.Any(e => e.Expression.Equals(item)) + ? sqlRemappingVisitor.Remap(item) + : subquery.GenerateOuterColumn(subqueryTableReference, item, groupByAliases[i] ?? "Key"); + newGroupByTerms.Add(newItem); + } + if (!emptyKey) + { + // If non-empty key then we need to regenerate the key selector + keySelector = new ReplacingExpressionVisitor(groupByTerms, newGroupByTerms).Visit(keySelector); + } + groupByTerms = newGroupByTerms; + } + + _groupBy.AddRange(groupByTerms); + + // We generate the cloned expression before changing identifier for this SelectExpression + // because we are going to erase grouping for cloned expression. + var clonedSelectExpression = Clone(); + var correlationPredicate = groupByTerms.Zip(clonedSelectExpression._groupBy) + .Select(e => sqlExpressionFactory.Equal(e.First, e.Second)) + .Aggregate((l, r) => sqlExpressionFactory.AndAlso(l, r)); + clonedSelectExpression._groupBy.Clear(); + clonedSelectExpression.ApplyPredicate(correlationPredicate); + + if (!_identifier.All(e => _groupBy.Contains(e.Column))) + { + _identifier.Clear(); + if (_groupBy.All(e => e is ColumnExpression)) + { + _identifier.AddRange(_groupBy.Select(e => ((ColumnExpression)e, e.TypeMapping!.KeyComparer))); + } + } + + return new GroupByShaperExpression( + keySelector, + new ShapedQueryExpression( + clonedSelectExpression, + new QueryExpressionReplacingExpressionVisitor(this, clonedSelectExpression).Visit(shaperExpression))); } - private void AppendGroupBy(Expression keySelector, List groupByTerms, List groupByAliases, string? name) + private void PopulateGroupByTerms(Expression keySelector, List groupByTerms, List groupByAliases, string? name) { Check.NotNull(keySelector, nameof(keySelector)); @@ -1189,23 +1257,23 @@ private void AppendGroupBy(Expression keySelector, List groupByTe case NewExpression newExpression: for (var i = 0; i < newExpression.Arguments.Count; i++) { - AppendGroupBy(newExpression.Arguments[i], groupByTerms, groupByAliases, newExpression.Members?[i].Name); + PopulateGroupByTerms(newExpression.Arguments[i], groupByTerms, groupByAliases, newExpression.Members?[i].Name); } break; case MemberInitExpression memberInitExpression: - AppendGroupBy(memberInitExpression.NewExpression, groupByTerms, groupByAliases, null); + PopulateGroupByTerms(memberInitExpression.NewExpression, groupByTerms, groupByAliases, null); foreach (var argument in memberInitExpression.Bindings) { var memberAssignment = (MemberAssignment)argument; - AppendGroupBy(memberAssignment.Expression, groupByTerms, groupByAliases, memberAssignment.Member.Name); + PopulateGroupByTerms(memberAssignment.Expression, groupByTerms, groupByAliases, memberAssignment.Member.Name); } break; case UnaryExpression unaryExpression when unaryExpression.NodeType == ExpressionType.Convert || unaryExpression.NodeType == ExpressionType.ConvertChecked: - AppendGroupBy(unaryExpression.Operand, groupByTerms, groupByAliases, name); + PopulateGroupByTerms(unaryExpression.Operand, groupByTerms, groupByAliases, name); break; default: diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index d1977373693..cb2ed4fda42 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -1195,7 +1195,9 @@ public GroupingElementReplacingExpressionVisitor( protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { if (methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.GetGenericMethodDefinition() == QueryableMethods.AsQueryable + && (methodCallExpression.Method.GetGenericMethodDefinition() == QueryableMethods.AsQueryable + || methodCallExpression.Method.GetGenericMethodDefinition() == EnumerableMethods.ToList + || methodCallExpression.Method.GetGenericMethodDefinition() == EnumerableMethods.ToArray) && methodCallExpression.Arguments[0] == _parameterExpression) { var currentTree = _cloningExpressionVisitor.Clone(_navigationExpansionExpression.CurrentTree); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs index b814c7bd6f5..1618e573add 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs @@ -436,6 +436,17 @@ from c in grouping.DefaultIfEmpty() g => new { Value = g.Key + g.Key, Average = g.Average(o => o.OrderID) })); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_with_aggregate_through_navigation_property(bool async) + { + return AssertQuery( + async, + ss => ss.Set().GroupBy(c => c.EmployeeID).Select( + g => new { max = g.Max(i => i.Customer.Region) }), + elementSorter: e => e.max); + } + #endregion #region GroupByAnonymousAggregate @@ -1909,7 +1920,7 @@ from o in ss.Set().GroupBy(o => o.CustomerID) .Where(g => g.Count() > 5) .Select(g => new { CustomerID = g.Key, LastOrderID = g.Max(o => o.OrderID) }) .Where(c1 => c.CustomerID == c1.CustomerID) - select c, + select c, entryCount: 63); } @@ -2833,17 +2844,6 @@ public virtual Task GroupBy_Distinct(bool async) ss => ss.Set().GroupBy(o => o.CustomerID).Distinct().Select(g => g.Key))); } - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] - public virtual Task GroupBy_with_aggregate_through_navigation_property(bool async) - { - return AssertQuery( - async, - ss => ss.Set().GroupBy(c => c.EmployeeID).Select( - g => new { max = g.Max(i => i.Customer.Region) }), - elementSorter: e => e.max); - } - #endregion #region GroupBySelectFirst @@ -2882,6 +2882,78 @@ public virtual Task GroupBy_Shadow3(bool async) .Select(g => EF.Property(g.First(), "Title"))); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_select_grouping_list(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .GroupBy(e => e.City) + .Select(g => new { g.Key, List = g.ToList() }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + AssertEqual(e.Key, a.Key); + AssertCollection(e.List, a.List); + }, + entryCount: 91); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_select_grouping_array(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .GroupBy(e => e.City) + .Select(g => new { g.Key, List = g.ToArray() }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + AssertEqual(e.Key, a.Key); + AssertCollection(e.List, a.List); + }, + entryCount: 91); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_select_grouping_composed_list(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .GroupBy(e => e.City) + .Select(g => new { g.Key, List = g.Where(c => c.CustomerID.StartsWith("A")).ToList() }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + AssertEqual(e.Key, a.Key); + AssertCollection(e.List, a.List); + }, + entryCount: 4); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_select_grouping_composed_list_2(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .GroupBy(e => e.City) + .Select(g => new { g.Key, List = g.OrderBy(c => c.CustomerID).ToList() }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + AssertEqual(e.Key, a.Key); + AssertCollection(e.List, a.List); + }, + entryCount: 91); + } + #endregion #region GroupByEntityType diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs index 85e4874710e..356ebcdac45 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs @@ -661,7 +661,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] LEFT JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_collection_with_groupby_in_subquery_and_filter_before_groupby(bool async) @@ -686,7 +686,7 @@ WHERE [l0].[Id] > 3 WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] LEFT JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_collection_with_groupby_in_subquery_and_filter_after_groupby(bool async) @@ -710,7 +710,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] LEFT JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_reference_collection_order_by_reference_navigation(bool async) @@ -2000,16 +2000,16 @@ public override async Task Skip_Take_on_grouping_element_into_non_entity(bool as await base.Skip_Take_on_grouping_element_into_non_entity(async); AssertSql( - @"SELECT [t].[Date], [t0].[Name], [t0].[Date] + @"SELECT [t].[Date], [t0].[Name], [t0].[Id] FROM ( SELECT [l].[Date] FROM [LevelOne] AS [l] GROUP BY [l].[Date] ) AS [t] LEFT JOIN ( - SELECT [t1].[Name], [t1].[Date] + SELECT [t1].[Name], [t1].[Id], [t1].[Date] FROM ( - SELECT [l0].[Name], [l0].[Date], ROW_NUMBER() OVER(PARTITION BY [l0].[Date] ORDER BY [l0].[Name]) AS [row] + SELECT [l0].[Name], [l0].[Id], [l0].[Date], ROW_NUMBER() OVER(PARTITION BY [l0].[Date] ORDER BY [l0].[Name]) AS [row] FROM [LevelOne] AS [l0] ) AS [t1] WHERE (1 < [t1].[row]) AND ([t1].[row] <= 6) @@ -2039,7 +2039,7 @@ OFFSET 1 ROWS FETCH NEXT 5 ROWS ONLY ) AS [t1] LEFT JOIN [LevelTwo] AS [l0] ON [t1].[Id] = [l0].[OneToMany_Optional_Inverse2Id] ) AS [t0] -ORDER BY [t].[Date], [t0].[Name], [t0].[Date]"); +ORDER BY [t].[Date], [t0].[Name], [t0].[Id]"); } public override async Task Skip_Take_on_grouping_element_with_reference_include(bool async) @@ -2064,7 +2064,7 @@ OFFSET 1 ROWS FETCH NEXT 5 ROWS ONLY ) AS [t1] LEFT JOIN [LevelTwo] AS [l0] ON [t1].[Id] = [l0].[Level1_Optional_Id] ) AS [t0] -ORDER BY [t].[Date], [t0].[Name], [t0].[Date]"); +ORDER BY [t].[Date], [t0].[Name], [t0].[Id]"); } public override async Task Skip_Take_on_grouping_element_inside_collection_projection(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs index 58a87f74eec..509624571d8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs @@ -2228,9 +2228,9 @@ FROM [LevelOne] AS [l0] ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] -ORDER BY [t].[Name], [t0].[Name]", +ORDER BY [t].[Name], [t0].[Id]", // - @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Name] + @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Id] FROM ( SELECT [l].[Name] FROM [LevelOne] AS [l] @@ -2245,7 +2245,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] INNER JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_collection_with_groupby_in_subquery_and_filter_before_groupby(bool async) @@ -2269,9 +2269,9 @@ WHERE [l0].[Id] > 3 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] -ORDER BY [t].[Name], [t0].[Name]", +ORDER BY [t].[Name], [t0].[Id]", // - @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Name] + @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Id] FROM ( SELECT [l].[Name] FROM [LevelOne] AS [l] @@ -2288,7 +2288,7 @@ WHERE [l0].[Id] > 3 WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] INNER JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_collection_with_groupby_in_subquery_and_filter_after_groupby(bool async) @@ -2311,9 +2311,9 @@ FROM [LevelOne] AS [l0] ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] -ORDER BY [t].[Name], [t0].[Name]", +ORDER BY [t].[Name], [t0].[Id]", // - @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Name] + @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Id] FROM ( SELECT [l].[Name] FROM [LevelOne] AS [l] @@ -2329,7 +2329,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] INNER JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_reference_collection_order_by_reference_navigation(bool async) @@ -3111,7 +3111,7 @@ FROM [LevelOne] AS [l0] ) AS [t1] WHERE (1 < [t1].[row]) AND ([t1].[row] <= 6) ) AS [t0] ON [t].[Date] = [t0].[Date] -ORDER BY [t].[Date], [t0].[Date], [t0].[Name]"); +ORDER BY [t].[Date], [t0].[Date], [t0].[Name], [t0].[Id]"); } public override async Task Skip_Take_on_grouping_element_with_reference_include(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index ba668023866..f76c3ca9b08 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -2611,7 +2611,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] LEFT JOIN [LevelThree] AS [l2] ON [t0].[Id0] = [l2].[OneToMany_Optional_Inverse3Id] -ORDER BY [t].[Name], [t0].[Name], [t0].[Id0]"); +ORDER BY [t].[Name], [t0].[Id], [t0].[Id0]"); } public override async Task String_include_multiple_derived_navigation_with_same_name_and_same_type(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index eae4227f0e4..c2811621eb4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -5589,7 +5589,7 @@ GROUP BY [g].[Rank] LEFT JOIN ( SELECT [t1].[Nickname], [t1].[SquadId], [t1].[AssignedCityName], [t1].[CityOfBirthName], [t1].[Discriminator], [t1].[FullName], [t1].[HasSoulPatch], [t1].[LeaderNickname], [t1].[LeaderSquadId], [t1].[Rank], [t1].[Name], [t1].[Location], [t1].[Nation] FROM ( - SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], [c].[Name], [c].[Location], [c].[Nation], ROW_NUMBER() OVER(PARTITION BY [g0].[Rank] ORDER BY [g0].[Rank], [c].[Name]) AS [row] + SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], [c].[Name], [c].[Location], [c].[Nation], ROW_NUMBER() OVER(PARTITION BY [g0].[Rank] ORDER BY [g0].[Nickname], [g0].[SquadId], [c].[Name]) AS [row] FROM [Gears] AS [g0] INNER JOIN [Cities] AS [c] ON [g0].[CityOfBirthName] = [c].[Name] WHERE [g0].[HasSoulPatch] = CAST(1 AS bit) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs index 1a474be074f..3e5f589eb62 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs @@ -1893,7 +1893,7 @@ GROUP BY [e].[Title] LEFT JOIN ( SELECT [t1].[EmployeeID], [t1].[City], [t1].[Country], [t1].[FirstName], [t1].[ReportsTo], [t1].[Title] FROM ( - SELECT [e0].[EmployeeID], [e0].[City], [e0].[Country], [e0].[FirstName], [e0].[ReportsTo], [e0].[Title], ROW_NUMBER() OVER(PARTITION BY [e0].[Title] ORDER BY [e0].[Title]) AS [row] + SELECT [e0].[EmployeeID], [e0].[City], [e0].[Country], [e0].[FirstName], [e0].[ReportsTo], [e0].[Title], ROW_NUMBER() OVER(PARTITION BY [e0].[Title] ORDER BY [e0].[EmployeeID]) AS [row] FROM [Employees] AS [e0] WHERE ([e0].[Title] = N'Sales Representative') AND ([e0].[EmployeeID] = 1) ) AS [t1] @@ -1915,6 +1915,74 @@ FROM [Employees] AS [e] GROUP BY [e].[EmployeeID]"); } + public override async Task GroupBy_select_grouping_list(bool async) + { + await base.GroupBy_select_grouping_list(async); + + AssertSql( + @"SELECT [t].[City], [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] +FROM ( + SELECT [c].[City] + FROM [Customers] AS [c] + GROUP BY [c].[City] +) AS [t] +LEFT JOIN [Customers] AS [c0] ON [t].[City] = [c0].[City] +ORDER BY [t].[City]"); + } + + public override async Task GroupBy_select_grouping_array(bool async) + { + await base.GroupBy_select_grouping_array(async); + + AssertSql( + @"SELECT [t].[City], [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] +FROM ( + SELECT [c].[City] + FROM [Customers] AS [c] + GROUP BY [c].[City] +) AS [t] +LEFT JOIN [Customers] AS [c0] ON [t].[City] = [c0].[City] +ORDER BY [t].[City]"); + } + + public override async Task GroupBy_select_grouping_composed_list(bool async) + { + await base.GroupBy_select_grouping_composed_list(async); + + AssertSql( + @"SELECT [t].[City], [t0].[CustomerID], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [c].[City] + FROM [Customers] AS [c] + GROUP BY [c].[City] +) AS [t] +LEFT JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE [c0].[CustomerID] LIKE N'A%' +) AS [t0] ON ([t].[City] = [t0].[City]) OR ([t].[City] IS NULL AND [t0].[City] IS NULL) +ORDER BY [t].[City]"); + } + + public override async Task GroupBy_select_grouping_composed_list_2(bool async) + { + await base.GroupBy_select_grouping_composed_list_2(async); + + AssertSql( + @"SELECT [t].[City], [t0].[CustomerID], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [c].[City] + FROM [Customers] AS [c] + GROUP BY [c].[City] +) AS [t] +LEFT JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] +) AS [t0] ON [t].[City] = [t0].[City] +ORDER BY [t].[City], [t0].[CustomerID]"); + } + + public override async Task Select_GroupBy_SelectMany(bool async) { await base.Select_GroupBy_SelectMany(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs index dc3d1e44671..f38630ac6cd 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs @@ -1097,7 +1097,7 @@ public override async Task Include_collection_Join_GroupBy_Select(bool async) await base.Include_collection_Join_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1106,9 +1106,9 @@ FROM [Orders] AS [o] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] INNER JOIN [Order Details] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 @@ -1116,7 +1116,7 @@ FROM [Orders] AS [o1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID], [o3].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID]"); } public override async Task Include_reference_Join_GroupBy_Select(bool async) @@ -1150,7 +1150,7 @@ public override async Task Join_Include_collection_GroupBy_Select(bool async) await base.Join_Include_collection_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1159,9 +1159,9 @@ FROM [Order Details] AS [o] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 @@ -1169,7 +1169,7 @@ FROM [Order Details] AS [o1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID], [o3].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID], [o3].[OrderID]"); } public override async Task Join_Include_reference_GroupBy_Select(bool async) @@ -1201,7 +1201,7 @@ public override async Task Include_collection_SelectMany_GroupBy_Select(bool asy await base.Include_collection_SelectMany_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1210,9 +1210,9 @@ CROSS JOIN [Order Details] AS [o0] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] CROSS JOIN [Order Details] AS [o2] WHERE [o1].[OrderID] = 10248 @@ -1220,7 +1220,7 @@ CROSS JOIN [Order Details] AS [o2] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID], [o3].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID]"); } public override async Task Include_reference_SelectMany_GroupBy_Select(bool async) @@ -1254,7 +1254,7 @@ public override async Task SelectMany_Include_collection_GroupBy_Select(bool asy await base.SelectMany_Include_collection_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1263,9 +1263,9 @@ CROSS JOIN [Orders] AS [o0] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] CROSS JOIN [Orders] AS [o2] WHERE [o1].[OrderID] = 10248 @@ -1273,7 +1273,7 @@ CROSS JOIN [Orders] AS [o2] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID], [o3].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID], [o3].[OrderID]"); } public override async Task SelectMany_Include_reference_GroupBy_Select(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs index 15f238a0fc6..7cb18e992ff 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs @@ -1477,7 +1477,7 @@ public override async Task Include_collection_Join_GroupBy_Select(bool async) await base.Include_collection_Join_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1486,18 +1486,18 @@ FROM [Orders] AS [o] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] INNER JOIN [Order Details] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]", +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]", // - @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID] + @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1506,9 +1506,9 @@ FROM [Orders] AS [o] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID] + SELECT [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] INNER JOIN [Order Details] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 @@ -1516,7 +1516,7 @@ FROM [Orders] AS [o1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] INNER JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]"); } public override async Task Include_reference_Join_GroupBy_Select(bool async) @@ -1550,7 +1550,7 @@ public override async Task Join_Include_collection_GroupBy_Select(bool async) await base.Join_Include_collection_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1559,18 +1559,18 @@ FROM [Order Details] AS [o] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]", +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID]", // - @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID] + @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1579,9 +1579,9 @@ FROM [Order Details] AS [o] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID] + SELECT [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 @@ -1589,7 +1589,7 @@ FROM [Order Details] AS [o1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] INNER JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID]"); } public override async Task Join_Include_reference_GroupBy_Select(bool async) @@ -1621,7 +1621,7 @@ public override async Task Include_collection_SelectMany_GroupBy_Select(bool asy await base.Include_collection_SelectMany_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1630,18 +1630,18 @@ CROSS JOIN [Order Details] AS [o0] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] CROSS JOIN [Order Details] AS [o2] WHERE [o1].[OrderID] = 10248 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]", +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]", // - @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID] + @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1650,9 +1650,9 @@ CROSS JOIN [Order Details] AS [o0] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID] + SELECT [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] CROSS JOIN [Order Details] AS [o2] WHERE [o1].[OrderID] = 10248 @@ -1660,7 +1660,7 @@ CROSS JOIN [Order Details] AS [o2] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] INNER JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]"); } public override async Task Include_reference_SelectMany_GroupBy_Select(bool async) @@ -1694,7 +1694,7 @@ public override async Task SelectMany_Include_collection_GroupBy_Select(bool asy await base.SelectMany_Include_collection_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1703,18 +1703,18 @@ CROSS JOIN [Orders] AS [o0] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] CROSS JOIN [Orders] AS [o2] WHERE [o1].[OrderID] = 10248 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]", +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID]", // - @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID] + @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1723,9 +1723,9 @@ CROSS JOIN [Orders] AS [o0] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID] + SELECT [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] CROSS JOIN [Orders] AS [o2] WHERE [o1].[OrderID] = 10248 @@ -1733,7 +1733,7 @@ CROSS JOIN [Orders] AS [o2] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] INNER JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID]"); } public override async Task SelectMany_Include_reference_GroupBy_Select(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index ffee3efbccc..293f3c337a0 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -6539,7 +6539,7 @@ LEFT JOIN ( FROM ( SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], CASE WHEN [o0].[Nickname] IS NOT NULL THEN N'Officer' - END AS [Discriminator], [c].[Name], [c].[Location], [c].[Nation], ROW_NUMBER() OVER(PARTITION BY [g0].[Rank] ORDER BY [g0].[Rank], [c].[Name]) AS [row] + END AS [Discriminator], [c].[Name], [c].[Location], [c].[Nation], ROW_NUMBER() OVER(PARTITION BY [g0].[Rank] ORDER BY [g0].[Nickname], [g0].[SquadId], [c].[Name]) AS [row] FROM [Gears] AS [g0] LEFT JOIN [Officers] AS [o0] ON ([g0].[Nickname] = [o0].[Nickname]) AND ([g0].[SquadId] = [o0].[SquadId]) INNER JOIN [Cities] AS [c] ON [g0].[CityOfBirthName] = [c].[Name] From 0816abaa5db4c45e707b2f11121bd598cbb12a83 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 16 Sep 2021 23:03:29 +0000 Subject: [PATCH 5/6] Update dependencies from https://github.com/dotnet/runtime build 20210916.4 (#26080) [release/6.0-rc2] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 44 ++++++++++++++++++++--------------------- eng/Versions.props | 22 ++++++++++----------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7d9bd40afe0..48f2563f63c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,49 +1,49 @@ - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb - + https://github.com/dotnet/runtime - b3a9b0b740cce9322b03200db0b4ab736d7cd12d + 0bc0c0ac28e74b5d107781a3caa456f9ef2be4bb diff --git a/eng/Versions.props b/eng/Versions.props index 1b0b6289d34..514a989fbb8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -15,17 +15,17 @@ False - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 - 6.0.0-rc.2.21464.21 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 + 6.0.0-rc.2.21466.4 3.7.0 From 99de4a66633525af356f4a0f55a55301ccb4bf82 Mon Sep 17 00:00:00 2001 From: Maurycy Markowski Date: Thu, 16 Sep 2021 16:07:55 -0700 Subject: [PATCH 6/6] Adding regression test for #15279 We already have regression tests for most of the scenarios mentioned in the issue report. Adding the one scenario that was missing. Resolves #15279 --- .../Query/NorthwindGroupByQueryTestBase.cs | 25 +++++++++++++++++ .../NorthwindGroupByQuerySqlServerTest.cs | 27 +++++++++++++++++++ .../Query/NorthwindGroupByQuerySqliteTest.cs | 6 +++++ 3 files changed, 58 insertions(+) diff --git a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs index 1618e573add..6f89bffc442 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs @@ -3300,6 +3300,31 @@ public virtual Task Complex_query_with_groupBy_in_subquery4(bool async) }); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Complex_query_with_group_by_in_subquery5(bool async) + { + return AssertQuery( + async, + ss => from od in ss.Set() + where od.Order.Customer.CustomerID == "ALFKI" + group od by od.ProductID into grouping + select new + { + Sum = grouping.Sum(x => x.ProductID + x.OrderID * 1000), + Subquery = (from c in ss.Set() + where c.CustomerID.Length < grouping.Min(x => x.OrderID / 100) + orderby c.CustomerID + select new { c.CustomerID, c.City }).ToList() + }, + elementSorter: e => e.Sum, + elementAsserter: (e, a) => + { + AssertEqual(e.Sum, a.Sum); + AssertCollection(e.Subquery, a.Subquery, elementSorter: ee => ee.CustomerID); + }); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_scalar_subquery(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs index 3e5f589eb62..6cf9201655c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs @@ -2847,6 +2847,33 @@ public override async Task Select_correlated_collection_after_GroupBy_aggregate_ //AssertSql(" "); } + public override async Task Complex_query_with_group_by_in_subquery5(bool async) + { + await base.Complex_query_with_group_by_in_subquery5(async); + + AssertSql( + @"SELECT [t].[c], [t].[ProductID], [t0].[CustomerID], [t0].[City] +FROM ( + SELECT COALESCE(SUM([o].[ProductID] + ([o].[OrderID] * 1000)), 0) AS [c], [o].[ProductID] + FROM [Order Details] AS [o] + INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] + LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] + WHERE [c].[CustomerID] = N'ALFKI' + GROUP BY [o].[ProductID] +) AS [t] +OUTER APPLY ( + SELECT [c0].[CustomerID], [c0].[City] + FROM [Customers] AS [c0] + WHERE CAST(LEN([c0].[CustomerID]) AS int) < ( + SELECT MIN([o1].[OrderID] / 100) + FROM [Order Details] AS [o1] + INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] + LEFT JOIN [Customers] AS [c1] ON [o2].[CustomerID] = [c1].[CustomerID] + WHERE ([c1].[CustomerID] = N'ALFKI') AND ([t].[ProductID] = [o1].[ProductID])) +) AS [t0] +ORDER BY [t].[ProductID], [t0].[CustomerID]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindGroupByQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindGroupByQuerySqliteTest.cs index 2ddf4a1dc46..6039d0f7ca4 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindGroupByQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindGroupByQuerySqliteTest.cs @@ -68,6 +68,12 @@ public override async Task Select_nested_collection_with_groupby(bool async) (await Assert.ThrowsAsync( () => base.Select_nested_collection_with_groupby(async))).Message); + public override async Task Complex_query_with_group_by_in_subquery5(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Complex_query_with_group_by_in_subquery5(async))).Message); + public override async Task Odata_groupby_empty_key(bool async) => await Assert.ThrowsAsync(() => base.Odata_groupby_empty_key(async)); }