2009-07-22 6 views
23

Je suis perplexe par ce problème de données facile. J'utilise le framework Entity et j'ai une base de données de produits. Ma page de résultats renvoie une liste paginée de ces produits. En ce moment, mes résultats sont triés par le nombre de ventes de chaque produit, de sorte que mon code ressemble à ceci:Comment ajouter ROW_NUMBER à une requête ou une entité LINQ?

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

Cela renvoie un ensemble de données IQueryable de mes entités, triées par le nombre de ventes.

Je veux que ma page de résultats montre le classement de chaque produit (dans l'ensemble de données). Mes résultats devraient ressembler à ceci:

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

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

J'attends que je veux juste ajouter une colonne dans mes résultats en utilisant la variable SQL ROW_NUMBER ... mais je ne sais pas comment ajouter cette colonne à mon résultats datatable.

Ma page résultante contient une boucle foreach, mais puisque j'utilise un ensemble paginé, je suppose que l'utilisation de ce nombre pour simuler un numéro de classement ne serait PAS la meilleure approche. Donc, ma question est, comment puis-je ajouter une colonne ROW_NUMBER à mes résultats de requête dans ce cas?

Répondre

0

Essayez cette

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 
} 

Tant que la liste reste dans le même ordre, ce qui devrait se produire à moins que le nombre de ventes change. Vous pourriez être en mesure d'obtenir un compte précis;

Il y a aussi cette façon de faire. Cela donne l'indice de départ de la page, plus il vous donne un petit ensemble de ventes à afficher sur la page. Vous commencez simplement à compter sur votre site Web à startIndex.

25

Utilisez le 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. J'étais en train d'implémenter ça à la main mais ça fonctionne. –

+0

Je ne sais pas si cela compte, mais cela va être difficile à transmettre, car c'est un objet anonyme. –

+2

Vous n'avez pas besoin d'utiliser un type anonyme. Créez un type non-anonyme et utilisez-le si vous en avez besoin. C'est juste un exemple. –

1

Voici une réponse longue haleine. Tout d'abord créer une classe pour loger le nombre/item paire comme ceci:

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

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

Vient ensuite une abstraction autour d'une page d'articles (numérotée ou 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(); 
    } 
} 

Une fois que vous avez que vous pouvez " paginate » une collection interrogeable particulier en utilisant ceci:

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

Et enfin une belle méthode d'extension pour couvrir le laid:

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

Ce qui signifie que vous pouvez faire:

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

En fait, à l'aide OrderBy puis Passer + Take génère ROW_NUMBER dans EF 4.5 (vous pouvez vérifier avec SQL Profiler).

Je cherchais un moyen de faire la même chose que vous demandez et je suis en mesure d'obtenir ce que je dois par une simplification de la réponse de Craig:

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

Par ailleurs, les utilisations SQL générées ROW_NUMBER> start et TOP rowsPerPage.

Questions connexes