2012-02-10 8 views
12

Ho bisogno di ordinare gli articoli archiviati in un database decrescente data di pubblicazione e quindi prendere i primi 20 record dopo l'articolo con .Come implementare SkipWhile con Linq su Sql senza prima caricare l'intera lista in memoria?

Questo è quello che vorrei fare con Linq:

IQueryable<Article> articles = 
    db.Articles 
    .OrderByDescending(a => a.PublicationDate) 
    .SkipWhile(a => a.Id != 100) 
    .Take(20); 

Tuttavia, questo genera una NotSupportedException perché SkipWhile non è supportata in LINQ to SQL (vedi here).

Una possibile soluzione è quella di eseguire la query e quindi applicare SkipWhile utilizzando Linq to Object:

IEnumerable<ArticleDescriptor> articles = 
    db.Articles 
    .OrderByDescending(a => a.PublicationDate) 
    .ToList() 
    .SkipWhile(a => a.Article.Id != 100) 
    .Take(20); 

Ma questo significa che ho bisogno di caricare l'intera lista ordinata in memoria e poi prendere 20 articoli dopo quello con Id == 100.

C'è un modo per evitare questo enorme consumo di memoria?

Più in generale, qual è il modo migliore per ottenere questo in SQL?

+1

requisito Interessante. Esiste una relazione tra 'Id' e' PublicationDate'? È quello che vuoi fare davvero per ordinarli per data, andare lungo quella lista fino ad arrivare a 'Id'' 100', e * poi * prendere i prossimi 20? – AakashM

+0

Sì, è esattamente quello che voglio. Non esiste alcuna relazione tra 'Id' e' PublicationDate'. La ragione di questo requisito è che ho bisogno di recuperare un certo numero di articoli pubblicati dopo un articolo specificato di cui conosco solo l'ID. Certo, se conoscessi il "PublicationDate" di quell'articolo, sarebbe molto più semplice. –

risposta

5

Se, come sto cercando di indovinare dal nome della colonna, PublicationDate non cambia, è possibile farlo in due query separate:

  • Stabilire la PublicationDate del Article con Id == 100
  • Recuperare i 20 articoli da quella data in poi

Qualcosa di simile:

var thresholdDate = db.Articles.Single(a => a.Id == 100).PublicationDate; 
var articles = 
    db.Articles 
    .Where(a => a.PublicationDate <= thresholdDate) 
    .OrderByDescending(a => a.PublicationDate) 
    .Take(20); 

Potrebbe anche essere che LINQ to SQL può tradurre questo:

var articles = 
    db.Articles 
    .Where(a => a.PublicationDate 
      <= db.Articles.Single(aa => aa.Id == 100).PublicationDate) 
    .OrderByDescending(a => a.PublicationDate) 
    .Take(20); 

ma che può essere troppo complesso per esso. Provalo e guarda

+0

Penso che questa sia una buona soluzione (anche pubblicata da @Atzoya), che potrebbe anche essere spostata in una stored procedure nel database. Suppongo che non possiamo farlo con una singola query SQL.L'unico problema qui è nell'evento (improbabile) che abbiamo molti articoli con ID diversi pubblicati nello stesso momento. Per risolvere questo problema, dobbiamo concordare una regola di ordinazione aggiuntiva durante il recupero degli articoli, come per ID decrescente: 'var articles = db.Articoli .Where (a => a.PublicationDate <= thresholdDate && a. Id <100) .OrderByDescending (a => a.PublicationDate) .ThenByDescending (a => a.Id) .Take (20); ' –

+0

Ho provato e funziona. LINQ to SQL riesce persino a tradurre l'intera query LINQ in una singola query SQL. –

+0

Posso suggerire di sostituire Single (a => a.Id == 100) con Single (aa => aa.Id == 100) per accettare questo come risposta? –

0

Non è la soluzione solo aggiungere un'istruzione where?

IQueryable<Article> articles = db.Articles.Where(a => a.id != 100).OrderByDescending(a => a.PublicationDate).Take(20); 
+0

Sì, ci ho pensato anch'io, ma osservate il requisito reale: ordinateli per data, seguite quella lista fino ad arrivare a id 100, ** poi ** prendete il prossimo 20. Strano. – AakashM

+1

Questo non funzionerà se l'articolo 5 è stato pubblicato _after_ l'articolo 10 (a causa di una seduta in bozza ad esempio) –

+0

Sì, ho capito male la domanda. – Tan

1

Si può provare in questo modo

var articles = 
    db.Articles 
    .Where(a => a.PublicationDate < db.Articles 
            .Where(aa => aa.Id==100) 
            .Select(aa => aa.PublicationDate) 
            .SingleOrDefault()) 
    .OrderByDescending(a => a.PublicationDate) 
    .Take(20); 
+0

+1 poiché questa è la stessa soluzione di quella pubblicata da AakashM, ma l'altra ha una spiegazione più chiara –

Problemi correlati