diff --git a/src/NPoco/Linq/ComplexSqlBuilder.cs b/src/NPoco/Linq/ComplexSqlBuilder.cs index 6a633704..c9a49099 100644 --- a/src/NPoco/Linq/ComplexSqlBuilder.cs +++ b/src/NPoco/Linq/ComplexSqlBuilder.cs @@ -38,7 +38,24 @@ public Sql GetSqlForProjection(Expression> projectionExpression, var sql = BuildJoin(_database, _sqlExpression, _joinSqlExpressions.Values.ToList(), selectMembers, false, distinct); return sql; } + public Sql GetSqlForProjection(Expression> projectionExpression, bool distinct, int skip, int rows) + { + var selectMembers = _database.DatabaseType.ExpressionVisitor(_database, _pocoData).SelectProjection(projectionExpression); + + ((ISqlExpression)_sqlExpression).SelectMembers.Clear(); + ((ISqlExpression)_sqlExpression).SelectMembers.AddRange(selectMembers); + + _sqlExpression.Limit(skip, rows); + if (!_joinSqlExpressions.Any()) + { + var finalsql = ((ISqlExpression)_sqlExpression).ApplyPaging(_sqlExpression.Context.ToSelectStatement(false, distinct), selectMembers.Select(x => x.PocoColumns), _joinSqlExpressions); + return new Sql(finalsql, _sqlExpression.Context.Params); + } + + var sql = BuildJoin(_database, _sqlExpression, _joinSqlExpressions.Values.ToList(), selectMembers, false, distinct); + return sql; + } public Sql BuildJoin(IDatabase database, SqlExpression sqlExpression, List joinSqlExpressions, List newMembers, bool count, bool distinct) { var modelDef = _pocoData; diff --git a/src/NPoco/Linq/SimpleQueryProvider.cs b/src/NPoco/Linq/SimpleQueryProvider.cs index d7780999..e90d5205 100644 --- a/src/NPoco/Linq/SimpleQueryProvider.cs +++ b/src/NPoco/Linq/SimpleQueryProvider.cs @@ -27,6 +27,7 @@ public interface IAsyncQueryResultProvider Task Any(Expression> whereExpression); Task> ToPage(int page, int pageSize); Task> ProjectTo(Expression> projectionExpression); + Task> ToProjectedPage(Expression> projectionExpression, int page, int pageSize); Task> Distinct(Expression> projectionExpression); Task> Distinct(); } @@ -53,6 +54,7 @@ public interface IQueryResultProvider Page ToPage(int page, int pageSize); List ProjectTo(Expression> projectionExpression); List Distinct(Expression> projectionExpression); + Page ToProjectedPage(Expression> projectionExpression, int page, int pageSize); List Distinct(); Task> ToListAsync(); Task ToArrayAsync(); @@ -71,6 +73,7 @@ public interface IQueryResultProvider Task AnyAsync(Expression> whereExpression); Task> ToPageAsync(int page, int pageSize); Task> ProjectToAsync(Expression> projectionExpression); + Task> ToProjectedPageAsync(Expression> projectionExpression, int page, int pageSize); Task> DistinctAsync(Expression> projectionExpression); Task> DistinctAsync(); } @@ -338,7 +341,29 @@ public Task> ProjectTo(Expression> projectionExpression var sql = _buildComplexSql.GetSqlForProjection(projectionExpression, false); return ExecuteQueryAsync(sql).Select(projectionExpression.Compile()).ToListAsync().AsTask(); } + public async Task> ToProjectedPage(Expression> projectionExpression, int page, int pageSize) + { + int offset = (page - 1) * pageSize; + + // Save the one-time command time out and use it for both queries + int saveTimeout = _database.OneTimeCommandTimeout; + + // Setup the paged result + var result = new Page(); + result.CurrentPage = page; + result.ItemsPerPage = pageSize; + result.TotalItems = await Count().ConfigureAwait(false); + result.TotalPages = result.TotalItems / pageSize; + if ((result.TotalItems % pageSize) != 0) + result.TotalPages++; + + _database.OneTimeCommandTimeout = saveTimeout; + var sql = _buildComplexSql.GetSqlForProjection(projectionExpression, false, offset, pageSize); + result.Items = await ExecuteQueryAsync(sql).Select(projectionExpression.Compile()).ToListAsync().AsTask(); + + return result; + } public Task> Distinct() { return ExecuteQueryAsync(new Sql(_sqlExpression.Context.ToSelectStatement(true, true), _sqlExpression.Context.Params)).ToListAsync().AsTask(); @@ -606,7 +631,29 @@ public QueryProvider(Database database) : base(database, null) var sql = _buildComplexSql.GetSqlForProjection(projectionExpression, false); return ExecuteQuery(sql).Select(projectionExpression.Compile()).ToList(); } + public new Page ToProjectedPage(Expression> projectionExpression, int page, int pageSize) + { + int offset = (page - 1) * pageSize; + // Save the one-time command time out and use it for both queries + int saveTimeout = _database.OneTimeCommandTimeout; + + // Setup the paged result + var result = new Page(); + result.CurrentPage = page; + result.ItemsPerPage = pageSize; + result.TotalItems = Count(); + result.TotalPages = result.TotalItems / pageSize; + if ((result.TotalItems % pageSize) != 0) + result.TotalPages++; + + _database.OneTimeCommandTimeout = saveTimeout; + + var sql = _buildComplexSql.GetSqlForProjection(projectionExpression, false, page, pageSize); + result.Items = ExecuteQuery(sql).Select(projectionExpression.Compile()).ToList(); + + return result; + } public new List Distinct(Expression> projectionExpression) { var sql = _buildComplexSql.GetSqlForProjection(projectionExpression, true); @@ -727,7 +774,10 @@ public Task> DistinctAsync(Expression> projectionExpres { return base.Distinct(projectionExpression); } - + public Task> ToProjectedPageAsync(Expression> projectionExpression, int skip, int rows) + { + return base.ToProjectedPage(projectionExpression, skip, rows); + } public Task> DistinctAsync() { return base.Distinct(); diff --git a/test/NPoco.Tests/Async/QueryAsyncTests.cs b/test/NPoco.Tests/Async/QueryAsyncTests.cs index ef4a388b..3dacd650 100644 --- a/test/NPoco.Tests/Async/QueryAsyncTests.cs +++ b/test/NPoco.Tests/Async/QueryAsyncTests.cs @@ -35,7 +35,13 @@ public async Task FetchByExpressionAndSelect() var users = await Database.Query().ProjectToAsync(x => new { x.Name }); Assert.AreEqual("Name1", users[0].Name); } - + [Test] + public async Task FetchByExpressionAndSelectProjection() + { + var records = await Database.Query().ToProjectedPageAsync(x => new { x.Name }, 2, 5); + Assert.AreEqual(records.Items.Count, 5); + Assert.AreEqual(records.Items[0].Name, "Name6"); + } [Test] public async Task PagingAsync() {