有点不确定您想使用什么技术从数据库中获取数据。
由于 Linq 关键字,我假设您需要一个 Linq 语句,该语句为您提供具有给定 pageSize 的项目页面。
但是,您不确定如何获取页面 X 的数据。您是否有要划分为页面的记录的 IQueryable(如在 Entity Framework 中 - 强烈推荐),或者您是否想更改您的SQL 语句,以便它会给你页面 X?
IQueryable 方法
假设您想要 T 类型的记录页面,并且您有一个 IQueryable<T> 来获取 T 类型的所有记录。
IQueryable<T> allRecords = ...;
您想将此序列分成页面。每个页面都有一个PageSize、一个PageNr和一个记录序列:
class Page<T>
{
public int PageSize {get; set;}
public int PageNr {get; set;}
public IEnumerable<T> Contents {get; set;}
}
现在把AllRecords分成一个页面序列,我用了一个扩展方法:
public static class PagingExtensions
{
public static IQueryable<Page<T>> ToPages<T>(this IQueryable<T> allRecords, int pageSize)
{
return allRecords.Select( (record, i) => new
{
PageNr = i / pageSize,
Record = record,
})
.GroupBy(item => item.PageNr)
// intermediate result: sequence of IGrouping<int, T>
// where key is pageNr
// and each element in the group are the records for this page
.Select(group => new Page<T>
{
PageNr = group.Key,
PageSize = pageSize,
Contents = (IEnumerable<T>) group
});
}
}
将 MyRecords 序列划分为页面的代码将是:
const int pageSize = 1000;
IQueryable<MyRecord> allMyRecords = ...
IQueryable<Page<MyRecord>> pages = allMyRecords.ToPages(1000);
// do what you want with the pages, for example:
foreach (Page<MyRecord> page in pages)
{
Console.WriteLine($"Page {page.PageNr}");
foreach (MyRecord record in Page.Contents)
{
Console.WriteLine(record.ToString());
}
}
请注意,所有使用的函数都使用延迟执行。在您枚举它们之前不会获取记录。
如果您希望能够在本地内存而不是数据库中的页面中划分集合,请使用 IEnumerable<T> 而不是 IQueryable<T>。
没有 IQueryable 的方法
如果您没有 IQueryable 来获取所有记录,您要么必须自己创建一个实现此功能的类,要么根据要获取的页面调整 SQL 查询。不过我不推荐第一种方法。
class Page<T>
{
public Page(SqlConnection conn, int pageNr, int pageSize)
{
this.PageNr = pageNr;
this.PageSize = pageSize;
}
private readonly SqlConnection conn;
public int PageSize {get; private set;}
public int PageNr {get; private set;}
public IEnumerable<T> ReadContents()
{
int offset = this.PageNr * this.PageSize;
int fetch = this.PageSize;
string cmdText = "SELECT col1, col2, ..."
+ " FROM ... "
+ " WHERE ... "
+ " ORDER BY -- "
// this is a MUST there must be ORDER BY statement
//-- the paging comes here
+ $" OFFSET {offset} ROWS"
+ $" FETCH NEXT {fetch} ROWS ONLY;";
using (SqlCommand cmd = new SqlCommand("cmdText, conn))
{
using (var sqlDataReader = cmd.ExecuteQuery())
{
List<T> readItems = sqlDataReader...;
// you know better than I how to use the SqlDataReader
return readItems
}
}
}
}
Fetch / Offset 而不是 Enumerable Skip / Take 的想法来自 stackoverflow 上的 Implement paging in SQL。