2012-02-10 9 views
12

Necesito ordenar los artículos almacenados en una base de datos al descender la fecha de publicación y luego tomar los primeros 20 registros después del artículo con Id == 100.¿Cómo implementar SkipWhile con Linq en Sql sin cargar primero toda la lista en la memoria?

Esto es lo que me gustaría hacer con LINQ:

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

Sin embargo, esto genera una NotSupportedException porque SkipWhile no está soportada en LINQ to SQL (ver here).

Una posible solución es ejecutar la consulta y luego aplicar SkipWhile usando LINQ a Objeto:

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

Pero esto significa que tengo que cargar la totalidad lista ordenada primero en la memoria y luego tomar 20 artículos después de que el uno con Id == 100.

¿Hay alguna manera de evitar este gran consumo de memoria?

Más en general, ¿cuál es la mejor manera de lograr esto en SQL?

+1

requisito interesante. ¿Hay alguna relación entre 'Id' y' PublicationDate'? ¿Es lo que realmente quieres hacer para ordenarlos por fecha, sigue esa lista hasta llegar a 'Id'' 100', y * luego * toma los siguientes 20? – AakashM

+0

Sí, eso es exactamente lo que quiero. No hay relación entre 'Id' y' PublicationDate'. El motivo de este requisito es que necesito recuperar una cierta cantidad de artículos publicados después de un artículo específico del cual solo conozco el 'Id'. Por supuesto, si conociera el 'PublicationDate' de ese artículo, sería mucho más fácil. –

Respuesta

5

Si, como yo supongo que desde el nombre de la columna, PublicationDate no cambia, puede hacerlo de dos consultas separadas:

  • establecer la PublicationDate del Article con Id == 100
  • Recuperar los 20 artículos de esa fecha en adelante

Algo así como:

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

Incluso podría ser que LINQ a SQL puede traducir esto:

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

pero que puede ser demasiado complejo para ello. Pruébalo y mira.

+0

Creo que esta es una buena solución (también publicada por @Atzoya), que también podría ser movida a un procedimiento almacenado en la base de datos. Supongo que no podemos hacerlo con una sola consulta SQL.El único problema aquí es en el evento (improbable) que tenemos muchos artículos con diferentes ID publicados al mismo tiempo. Pero para resolver esto tenemos que acordar una regla de pedido extra mientras buscamos los artículos, como por ejemplo, desciende los ID: 'var articles = db.Articles . Where (a => a.PublicationDate <= thresholdDate && a. Id <100) .OrderByDescending (a => a.PublicationDate) .ThenByDescending (a => a.Id) .Take (20); ' –

+0

Lo intenté y funciona. LINQ to SQL incluso logra traducir toda la consulta LINQ en una sola consulta SQL. –

+0

¿Puedo sugerirle que reemplace Single (a => a.Id == 100) con Single (aa => aa.Id == 100) para aceptar esto como una respuesta? –

0

¿No es la solución simplemente agregar una instrucción where?

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

Sí, pensé que también, pero mira el requisito real: ordenar por fecha, ir a lo largo de la lista hasta llegar a la identificación 100, ** luego ** tomar los próximos 20. Extraño. – AakashM

+1

Esto no funcionará si se publicó el artículo 5 _después_ del artículo 10 (debido a que se sienta en el borrador, por ejemplo) –

+0

Sí, no entendí la pregunta. – Tan

1

Usted puede tratar como esto

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 ya que esta es la misma solución que la publicada por AakashM, pero la otra tiene una explicación más clara –

Cuestiones relacionadas