Пейджинг с LINQ для объектов
Как вы реализуете пейджинг в запросе LINQ?
На самом деле, пока я был бы удовлетворен, если бы функция sql TOP могла быть имитирована. Тем не менее, я уверен, что потребность в полной поддержке поискового вызова появится рано или поздно.
var queryResult = from o in objects
where ...
select new
{
A = o.a,
B = o.b
}
????????? TOP 10????????
Ответы
Ответ 1
Вы ищете методы расширения Skip
и Take
. Skip
перемещается мимо первых N элементов в результате, возвращая остаток; Take
возвращает первые N элементов в результате, отбрасывая оставшиеся элементы.
См. MSDN для получения дополнительной информации о том, как использовать эти методы: http://msdn.microsoft.com/en-us/library/bb386988.aspx
Например:
int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
.Skip(numberOfObjectsPerPage * pageNumber)
.Take(numberOfObjectsPerPage);
Ответ 2
Использование Skip
и Take
- это, безусловно, путь. Если бы я это реализовал, я бы, вероятно, написал свой собственный метод расширения для обработки подкачки (чтобы сделать код более читаемым). Конечно, реализация может использовать Skip
и Take
:
static class PagingUtils {
public static IEnumerable<T> Page<T>(this IEnumerable<T> en, int pageSize, int page) {
return en.Skip(page * pageSize).Take(pageSize);
}
public static IQueryable<T> Page<T>(this IQueryable<T> en, int pageSize, int page) {
return en.Skip(page * pageSize).Take(pageSize);
}
}
Класс определяет два метода расширения: один для IEnumerable
и один для IQueryable
, что означает, что вы можете использовать его как с LINQ to Objects, так и с LINQ to SQL (при написании запроса к базе данных компилятор выбирает IQueryable
версия).
В зависимости от требований к поисковому запросу вы также можете добавить некоторые дополнительные действия (например, для обработки отрицательных значений pageSize
или page
). Ниже приведен пример использования этого метода расширения в вашем запросе:
var q = (from p in products
where p.Show == true
select new { p.Name }).Page(10, pageIndex);
Ответ 3
Вот мой подход к подкачке при использовании LINQ для объектов:
public static IEnumerable<IEnumerable<T>> Page<T>(this IEnumerable<T> source, int pageSize)
{
Contract.Requires(source != null);
Contract.Requires(pageSize > 0);
Contract.Ensures(Contract.Result<IEnumerable<IEnumerable<T>>>() != null);
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
var currentPage = new List<T>(pageSize)
{
enumerator.Current
};
while (currentPage.Count < pageSize && enumerator.MoveNext())
{
currentPage.Add(enumerator.Current);
}
yield return new ReadOnlyCollection<T>(currentPage);
}
}
}
Затем это можно использовать так:
var items = Enumerable.Range(0, 12);
foreach(var page in items.Page(3))
{
// Do something with each page
foreach(var item in page)
{
// Do something with the item in the current page
}
}
Ни один из этих мусора Skip
и Take
, который будет крайне неэффективен, если вас интересует несколько страниц.
Ответ 4
( for o in objects
where ...
select new
{
A=o.a,
B=o.b
})
.Skip((page-1)*pageSize)
.Take(pageSize)
Ответ 5
РЕДАКТИРОВАТЬ - Удалено Пропустить (0), поскольку это не необходимо
var queryResult = (from o in objects where ...
select new
{
A = o.a,
B = o.b
}
).Take(10);
Ответ 6
Не знаю, поможет ли это кому-либо, но я нашел его полезным для своих целей:
private static IEnumerable<T> PagedIterator<T>(IEnumerable<T> objectList, int PageSize)
{
var page = 0;
var recordCount = objectList.Count();
var pageCount = (int)((recordCount + PageSize)/PageSize);
if (recordCount < 1)
{
yield break;
}
while (page < pageCount)
{
var pageData = objectList.Skip(PageSize*page).Take(PageSize).ToList();
foreach (var rd in pageData)
{
yield return rd;
}
page++;
}
}
Чтобы использовать это, у вас будет некоторый запрос linq и передайте результат вместе с размером страницы в цикл foreach:
var results = from a in dbContext.Authors
where a.PublishDate > someDate
orderby a.Publisher
select a;
foreach(var author in PagedIterator(results, 100))
{
// Do Stuff
}
Таким образом, это приведет к повторению каждого автора, получающего 100 авторов за раз.
Ответ 7
var pages = items.Select((item, index) => new { item, Page = index / batchSize }).GroupBy(g => g.Page);
Batchsize, очевидно, будет целым числом. Это использует тот факт, что целые числа просто отбрасывают десятичные знаки.
Я наполовину шучу с этим ответом, но он будет делать то, что вы хотите, и потому, что он отложен, вы не будете подвергать большому штрафу за производительность, если вы делаете
pages.First(p => p.Key == thePage)
Это решение не для LinqToEntities, я даже не знаю, может ли это превратить это в хороший запрос.
Ответ 8
Я использую этот метод расширения:
public static IQueryable<T> Page<T, TResult>(this IQueryable<T> obj, int page, int pageSize, System.Linq.Expressions.Expression<Func<T, TResult>> keySelector, bool asc, out int rowsCount)
{
rowsCount = obj.Count();
int innerRows = rowsCount - (page * pageSize);
if (innerRows < 0)
{
innerRows = 0;
}
if (asc)
return obj.OrderByDescending(keySelector).Take(innerRows).OrderBy(keySelector).Take(pageSize).AsQueryable();
else
return obj.OrderBy(keySelector).Take(innerRows).OrderByDescending(keySelector).Take(pageSize).AsQueryable();
}
public IEnumerable<Data> GetAll(int RowIndex, int PageSize, string SortExpression)
{
int totalRows;
int pageIndex = RowIndex / PageSize;
List<Data> data= new List<Data>();
IEnumerable<Data> dataPage;
bool asc = !SortExpression.Contains("DESC");
switch (SortExpression.Split(' ')[0])
{
case "ColumnName":
dataPage = DataContext.Data.Page(pageIndex, PageSize, p => p.ColumnName, asc, out totalRows);
break;
default:
dataPage = DataContext.vwClientDetails1s.Page(pageIndex, PageSize, p => p.IdColumn, asc, out totalRows);
break;
}
foreach (var d in dataPage)
{
clients.Add(d);
}
return data;
}
public int CountAll()
{
return DataContext.Data.Count();
}
Ответ 9
public LightDataTable PagerSelection(int pageNumber, int setsPerPage, Func<LightDataRow, bool> prection = null)
{
this.setsPerPage = setsPerPage;
this.pageNumber = pageNumber > 0 ? pageNumber - 1 : pageNumber;
if (!ValidatePagerByPageNumber(pageNumber))
return this;
var rowList = rows.Cast<LightDataRow>();
if (prection != null)
rowList = rows.Where(prection).ToList();
if (!rowList.Any())
return new LightDataTable() { TablePrimaryKey = this.tablePrimaryKey };
//if (rowList.Count() < (pageNumber * setsPerPage))
// return new LightDataTable(new LightDataRowCollection(rowList)) { TablePrimaryKey = this.tablePrimaryKey };
return new LightDataTable(new LightDataRowCollection(rowList.Skip(this.pageNumber * setsPerPage).Take(setsPerPage).ToList())) { TablePrimaryKey = this.tablePrimaryKey };
}
это то, что я сделал.
Обычно вы начинаете с 1, но в IList вы начинаете с 0.
поэтому, если у вас есть 152 строки, которые означают, что у вас есть 8 пейджинга, но в IList у вас всего 7.
хоп это может сделать для вас понятным
Ответ 10
var results = (medicineInfo.OrderBy(x=>x.id)
.Skip((pages -1) * 2)
.Take(2));
Ответ 11
Существует два основных варианта:
.NET >= 4.0
Динамическая LINQ:
- Добавить с помощью System.Linq.Dynamic; вверху.
- Использование:
var people = people.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();
Вы также можете получить его NuGet.
.NET < 4,0
Методы расширения:
private static readonly Hashtable accessors = new Hashtable();
private static readonly Hashtable callSites = new Hashtable();
private static CallSite<Func<CallSite, object, object>> GetCallSiteLocked(string name) {
var callSite = (CallSite<Func<CallSite, object, object>>)callSites[name];
if(callSite == null)
{
callSites[name] = callSite = CallSite<Func<CallSite, object, object>>.Create(
Binder.GetMember(CSharpBinderFlags.None, name, typeof(AccessorCache),
new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
}
return callSite;
}
internal static Func<dynamic,object> GetAccessor(string name)
{
Func<dynamic, object> accessor = (Func<dynamic, object>)accessors[name];
if (accessor == null)
{
lock (accessors )
{
accessor = (Func<dynamic, object>)accessors[name];
if (accessor == null)
{
if(name.IndexOf('.') >= 0) {
string[] props = name.Split('.');
CallSite<Func<CallSite, object, object>>[] arr = Array.ConvertAll(props, GetCallSiteLocked);
accessor = target =>
{
object val = (object)target;
for (int i = 0; i < arr.Length; i++)
{
var cs = arr[i];
val = cs.Target(cs, val);
}
return val;
};
} else {
var callSite = GetCallSiteLocked(name);
accessor = target =>
{
return callSite.Target(callSite, (object)target);
};
}
accessors[name] = accessor;
}
}
}
return accessor;
}
public static IOrderedEnumerable<dynamic> OrderBy(this IEnumerable<dynamic> source, string property)
{
return Enumerable.OrderBy<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> OrderByDescending(this IEnumerable<dynamic> source, string property)
{
return Enumerable.OrderByDescending<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> ThenBy(this IOrderedEnumerable<dynamic> source, string property)
{
return Enumerable.ThenBy<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> ThenByDescending(this IOrderedEnumerable<dynamic> source, string property)
{
return Enumerable.ThenByDescending<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
}