Как добавить ROW_NUMBER в запрос LINQ или Entity?
Я смущен этой простой проблемой данных.
Я использую инфраструктуру Entity и имею базу данных продуктов. На моей странице результатов отображается разбитый список этих продуктов. Сейчас мои результаты упорядочены по количеству продаж каждого продукта, поэтому мой код выглядит следующим образом:
return Products.OrderByDescending(u => u.Sales.Count());
Это возвращает набор данных IQueryable для моих сущностей, отсортированный по количеству продаж.
Я хочу, чтобы страница моих результатов показывала ранг каждого продукта (в наборе данных). Мои результаты должны выглядеть так:
Page #1
1. Bananas
2. Apples
3. Coffee
Page #2
4. Cookies
5. Ice Cream
6. Lettuce
Я ожидаю, что просто хочу добавить столбец в свои результаты, используя переменную SQL ROW_NUMBER... но я не знаю, как добавить этот столбец в мои данные. datatable.
Моя итоговая страница содержит цикл foreach, но поскольку я использую разбитый на группы набор, я предполагаю, что использование этого числа для подделки номера ранжирования НЕ будет лучшим подходом.
Итак, мой вопрос: как мне добавить столбец ROW_NUMBER в мои результаты запроса в этом случае?
Ответы
Ответ 1
Используйте индексированную перегрузку Select:
var start = page * rowsPerPage;
Products.OrderByDescending(u => u.Sales.Count())
.Skip(start)
.Take(rowsPerPage)
.AsEnumerable()
.Select((u, index) => new { Product = u, Index = index + start });
Ответ 2
Вот длинный ответ. Сначала создайте класс для размещения пары число/элемент так:
public class NumberedItem<T>
{
public readonly int Number;
public readonly T Item;
public NumberedItem(int number, T item)
{
Item = item;
Number = number;
}
}
Далее идет абстракция вокруг страницы элементов (пронумерована или нет):
class PageOf<T> : IEnumerable<T>
{
private readonly int startsAt;
private IEnumerable<T> items;
public PageOf(int startsAt, IEnumerable<T> items)
{
this.startsAt = startsAt;
this.items = items;
}
public IEnumerable<NumberedItem<T>> NumberedItems
{
get
{
int index = 0;
foreach (var item in items)
yield return new NumberedItem<T>(startsAt + index++, item);
yield break;
}
}
public IEnumerator<T> GetEnumerator()
{
foreach (var item in items)
yield return item;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
После этого вы можете "Paginate" создать определенную запрашиваемую коллекцию, используя следующую команду:
class PaginatedQueryable<T>
{
private readonly int PageSize;
private readonly IQueryable<T> Source;
public PaginatedQueryable(int PageSize, IQueryable<T> Source)
{
this.PageSize = PageSize;
this.Source = Source;
}
public PageOf<T> Page(int pageNum)
{
var start = (pageNum - 1) * PageSize;
return new PageOf<T>(start + 1, Source.Skip(start).Take(PageSize));
}
}
И, наконец, хороший метод расширения для укрощения:
static class PaginationExtension
{
public static PaginatedQueryable<T> InPagesOf<T>(this IQueryable<T> target, int PageSize)
{
return new PaginatedQueryable<T>(PageSize, target);
}
}
Что вы теперь можете сделать:
var products = Products.OrderByDescending(u => u.Sales.Count()).InPagesOf(20).Page(1);
foreach (var product in products.NumberedItems)
{
Console.WriteLine("{0} {1}", product.Number, product.Item);
}
Ответ 3
Фактически, используя OrderBy, а затем Skip + Take генерирует ROW_NUMBER в EF 4.5 (вы можете проверить с помощью SQL Profiler).
Я искал способ сделать то же самое, о чем вы просите, и я смог получить то, что мне нужно, упростив ответ Крэйга:
var start = page * rowsPerPage;
Products.OrderByDescending(u => u.Sales.Count())
.Skip(start)
.Take(rowsPerPage)
.ToList();
Кстати, сгенерированный SQL использует ROW_NUMBER > start и TOP rowsPerPage.
Ответ 4
Попробуйте это
var x = Products.OrderByDecending(u => u.Sales.Count());
var y = x.ToList();
for(int i = 0; i < y.Count; i++) {
int myNumber = i; // this is your order number
}
Пока список остается в том же порядке, что должно произойти, если номер продаж не изменится. Вы можете получить точный счет;
Существует также этот способ сделать это.
var page = 2;
var count = 10;
var startIndex = page * count;
var x = Products.OrderByDecending(u => u.Sales.Count());
var y = x.Skip(startIndex).Take(count);
Это дает начальный индекс для страницы, плюс он дает вам небольшой набор продаж для отображения на странице. Вы только начинаете подсчет своего веб-сайта в startIndex.