diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/Internal/RelationalResultOperatorHandler.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/Internal/RelationalResultOperatorHandler.cs index a7f04c66046..dd6f558ffc3 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/Internal/RelationalResultOperatorHandler.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/Internal/RelationalResultOperatorHandler.cs @@ -379,6 +379,11 @@ var collectionSelectExpression private static Expression HandleCount(HandlerContext handlerContext) { + if (handlerContext.SelectExpression.Offset != null) + { + handlerContext.SelectExpression.PushDownSubquery(); + } + handlerContext.SelectExpression .SetProjectionExpression( new SqlFunctionExpression( @@ -579,6 +584,11 @@ private static Expression HandleLast(HandlerContext handlerContext) private static Expression HandleLongCount(HandlerContext handlerContext) { + if (handlerContext.SelectExpression.Offset != null) + { + handlerContext.SelectExpression.PushDownSubquery(); + } + handlerContext.SelectExpression .SetProjectionExpression( new SqlFunctionExpression( diff --git a/src/Microsoft.EntityFrameworkCore.Specification.Tests/AsyncQueryTestBase.cs b/src/Microsoft.EntityFrameworkCore.Specification.Tests/AsyncQueryTestBase.cs index 61af66f21dc..6158aea667c 100644 --- a/src/Microsoft.EntityFrameworkCore.Specification.Tests/AsyncQueryTestBase.cs +++ b/src/Microsoft.EntityFrameworkCore.Specification.Tests/AsyncQueryTestBase.cs @@ -3593,6 +3593,33 @@ public virtual async Task Select_bitwise_and_with_logical_and() } } + [ConditionalFact] + public virtual async Task Skip_CountAsync() + { + await AssertQuery( + cs => cs.Skip(7).CountAsync()); + } + + [ConditionalFact] + public virtual async Task Skip_LongCountAsync() + { + await AssertQuery( + cs => cs.Skip(7).LongCountAsync()); + } + + [ConditionalFact] + public virtual async Task OrderBy_Skip_CountAsync() + { + await AssertQuery( + cs => cs.OrderBy(c => c.Country).Skip(7).CountAsync()); + } + + [ConditionalFact] + public virtual async Task OrderBy_Skip_LongCountAsync() + { + await AssertQuery( + cs => cs.OrderBy(c => c.Country).Skip(7).LongCountAsync()); + } protected NorthwindContext CreateContext() { @@ -3620,6 +3647,20 @@ private async Task AssertQuery( } } + private async Task AssertQuery( + Func, Task> query, + bool assertOrder = false) + where TItem : class + { + using (var context = CreateContext()) + { + TestHelpers.AssertResults( + new[] { await query(NorthwindData.Set()) }, + new[] { await query(context.Set()) }, + assertOrder); + } + } + private async Task AssertQuery( Func, Task> query, bool assertOrder = false) diff --git a/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs b/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs index 5278bbe3eaf..75b3dd13180 100644 --- a/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs +++ b/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs @@ -6767,6 +6767,34 @@ from e2 in ClientDefaultIfEmpty(grouping) select new { City1 = e1.City, City2 = e2 != null ? e2.City : null }); } + [ConditionalFact] + public virtual void Skip_Count() + { + AssertQuery( + cs => cs.Skip(7).Count()); + } + + [ConditionalFact] + public virtual void Skip_LongCount() + { + AssertQuery( + cs => cs.Skip(7).LongCount()); + } + + [ConditionalFact] + public virtual void OrderBy_Skip_Count() + { + AssertQuery( + cs => cs.OrderBy(c => c.Country).Skip(7).Count()); + } + + [ConditionalFact] + public virtual void OrderBy_Skip_LongCount() + { + AssertQuery( + cs => cs.OrderBy(c => c.Country).Skip(7).LongCount()); + } + private static IEnumerable ClientDefaultIfEmpty(IEnumerable source) { return source?.Count() == 0 ? new[] { default(TElement) } : source; diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs index 164a0e22209..e7798b6a94e 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs @@ -6827,6 +6827,74 @@ FROM [Employees] AS [e1] Sql); } + public override void Skip_Count() + { + base.Skip_Count(); + + Assert.Equal( + @"@__p_0: 7 + +SELECT COUNT(*) +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY (SELECT 1) + OFFSET @__p_0 ROWS +) AS [t]", + Sql); + } + + public override void Skip_LongCount() + { + base.Skip_LongCount(); + + Assert.Equal( + @"@__p_0: 7 + +SELECT COUNT_BIG(*) +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY (SELECT 1) + OFFSET @__p_0 ROWS +) AS [t]", + Sql); + } + + public override void OrderBy_Skip_Count() + { + base.OrderBy_Skip_Count(); + + Assert.Equal( + @"@__p_0: 7 + +SELECT COUNT(*) +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY [c].[Country] + OFFSET @__p_0 ROWS +) AS [t]", + Sql); + } + + public override void OrderBy_Skip_LongCount() + { + base.OrderBy_Skip_LongCount(); + + Assert.Equal( + @"@__p_0: 7 + +SELECT COUNT_BIG(*) +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + ORDER BY [c].[Country] + OFFSET @__p_0 ROWS +) AS [t]", + Sql); + } + private const string FileLineEnding = @" "; diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/RowNumberPagingTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/RowNumberPagingTest.cs index aa12158eb52..f1b98027cce 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/RowNumberPagingTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/RowNumberPagingTest.cs @@ -388,6 +388,82 @@ THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) Sql); } + public override void Skip_Count() + { + base.Skip_Count(); + + Assert.Equal( + @"@__p_0: 7 + +SELECT COUNT(*) +FROM ( + SELECT [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].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(ORDER BY @@RowCount) AS [__RowNumber__] + FROM [Customers] AS [c] + ) AS [t0] + WHERE [t0].[__RowNumber__] > @__p_0 +) AS [t]", + Sql); + } + + public override void Skip_LongCount() + { + base.Skip_LongCount(); + + Assert.Equal( + @"@__p_0: 7 + +SELECT COUNT_BIG(*) +FROM ( + SELECT [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].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(ORDER BY @@RowCount) AS [__RowNumber__] + FROM [Customers] AS [c] + ) AS [t0] + WHERE [t0].[__RowNumber__] > @__p_0 +) AS [t]", + Sql); + } + + public override void OrderBy_Skip_Count() + { + base.OrderBy_Skip_Count(); + + Assert.Equal( + @"@__p_0: 7 + +SELECT COUNT(*) +FROM ( + SELECT [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].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(ORDER BY [c].[Country]) AS [__RowNumber__] + FROM [Customers] AS [c] + ) AS [t0] + WHERE [t0].[__RowNumber__] > @__p_0 +) AS [t]", + Sql); + } + + public override void OrderBy_Skip_LongCount() + { + base.OrderBy_Skip_LongCount(); + + Assert.Equal( + @"@__p_0: 7 + +SELECT COUNT_BIG(*) +FROM ( + SELECT [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].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], ROW_NUMBER() OVER(ORDER BY [c].[Country]) AS [__RowNumber__] + FROM [Customers] AS [c] + ) AS [t0] + WHERE [t0].[__RowNumber__] > @__p_0 +) AS [t]", + Sql); + } + private const string FileLineEnding = @" ";