Немного неизвестно, какую технику вы хотите использовать для получения данных из базы данных.
Из-за ключевого слова Linq я предполагаю, что вам нужен оператор Linq, который дает вам страницу элементов с заданным размером страницы.
Однако неизвестно, как вы хотите получить данные для страницы 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(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/Офсетный вместо перечислимого Skip/Take пришла Implement paging in SQL на StackOverflow.
Это действительно потрясающе. Большое спасибо за предоставление ответа, и я действительно ценю ваши усилия –