2009-07-22 10 views
23

Sono stumped da questo facile problema di dati.Come aggiungere ROW_NUMBER a una query LINQ o Entity?

Sto utilizzando il framework Entity e un database di prodotti. La mia pagina dei risultati restituisce un elenco impaginato di questi prodotti. In questo momento i miei risultati sono ordinati per il numero di vendite di ogni prodotto, quindi il mio codice simile a questo:

return Products.OrderByDescending(u => u.Sales.Count()); 

Ciò restituisce un set di dati IQueryable dei miei soggetti, in ordine di numero di vendite.

Desidero che la pagina dei risultati mostri il rango di ciascun prodotto (nel set di dati). I miei risultati dovrebbe essere simile a questo:

Page #1 
1. Bananas 
2. Apples 
3. Coffee 

Page #2 
4. Cookies 
5. Ice Cream 
6. Lettuce 

mi aspetto che voglio solo aggiungere una colonna nei miei risultati utilizzando la variabile SQL ROW_NUMBER ... ma non so come aggiungere questa colonna alla mia risultati datatable.

La mia pagina risultante contiene un ciclo foreach, ma dal momento che sto usando un set impaginato, suppongo che l'utilizzo di quel numero per falsificare un numero di classifica NON sarebbe l'approccio migliore.

Quindi la mia domanda è: come posso aggiungere una colonna ROW_NUMBER ai risultati della mia query in questo caso?

risposta

0

Prova questa

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 
} 

Finché la lista rimane nello stesso ordine, che dovrebbe avvenire a meno che il numero di vendita cambia. Potresti essere in grado di ottenere un conteggio accurato;

C'è anche questo modo di farlo.

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); 

Questo dà l'indice di inizio per la pagina, in più ti dà un piccolo insieme di vendite da visualizzare sulla pagina. Hai appena iniziato il conteggio sul tuo sito web a startIndex.

25

Utilizzare la indexed overload of 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 }); 
+1

Haha. Sono stato in grado di implementarlo a mano ma funziona. –

+0

Non so se questo è importante, ma questo sarà difficile da aggirare, perché è un oggetto anonimo. –

+2

Non è necessario utilizzare un tipo anonimo. Crea un tipo non anonimo e usalo se necessario. È solo un esempio –

1

Ecco una risposta prolissa. In primo luogo creare una classe per ospitare la coppia/numero di voce in questo modo:

public class NumberedItem<T> 
{ 
    public readonly int Number; 
    public readonly T Item; 

    public NumberedItem(int number, T item) 
    { 
     Item = item; 
     Number = number; 
    } 
} 

Segue un'astrazione intorno una pagina di elementi (numerati e non):

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(); 
    } 
} 

Una volta che avete che è possibile " impaginare" una particolare collezione interrogabile tramite questo:

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)); 
    } 
} 

E infine un metodo di estensione piacevole per coprire il brutto:

static class PaginationExtension 
{ 
    public static PaginatedQueryable<T> InPagesOf<T>(this IQueryable<T> target, int PageSize) 
    { 
     return new PaginatedQueryable<T>(PageSize, target); 
    } 
} 

Il che significa che ora si può fare questo:

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); 
} 
1

realtà utilizzando OrderBy e poi Skip + Prendere genera ROW_NUMBER a EF 4.5 (è possibile controllare con SQL Profiler).

ero alla ricerca di un modo per fare la stessa cosa si sta chiedendo e sono stato in grado di ottenere quello che mi serve attraverso una semplificazione della risposta di Craig:

var start = page * rowsPerPage; 
Products.OrderByDescending(u => u.Sales.Count()) 
    .Skip(start) 
    .Take(rowsPerPage) 
    .ToList(); 

Tra l'altro, gli usi SQL generate ROW_NUMBER> start e TOP rowsPerPage.

Problemi correlati