Pregunta de LINQ to Entities sobre orderby y colecciones nulas

Actualmente estoy desarrollando un foro. Soy nuevo en LINQ y EF. En mi foro tengo una pantalla que muestra una lista de temas con los temas más recientes primero.

El problema es que "más reciente" es relativo a las respuestas del tema. Por lo tanto, no quiero ordenar la lista por la fecha de publicación del tema, sino que quiero ordenar la lista por la fecha de publicación de la última respuesta del tema. Para que los temas con respuestas más recientes vuelvan a aparecer en la parte superior de la lista. Esto es bastante simple si supiera que cada tema tiene al menos una respuesta; Solo haría esto:

var topicsQuery = from x in board.Topics
                  orderby x.Replies.Last().PostedDate descending
                  select x;

Sin embargo, en muchos casos el tema no tiene respuestas. En cuyo caso, me gustaría usar la fecha de publicación del tema. ¿Hay alguna forma dentro de mi consulta de linq para ordenar por x.PostedDate en caso de que el tema no tenga respuestas? Estoy confundido por esto y cualquier ayuda sería apreciada. Con la consulta anterior, se interrumpe en temas sin respuesta debido a la x.Replies.Last() lo que supone que hay respuestas. LastOrDefault() no funciona porque necesito acceder a la propiedad PostedDate que también asume que existe una respuesta.

Gracias de antemano por cualquier información.

preguntado el 09 de enero de 11 a las 05:01

Probablemente podría hacerlo simplemente obteniendo todas las respuestas, recorriéndolas y usando declaraciones if para determinar si hay respuestas y construyendo una nueva lista de esa manera. Pero si hay una forma de hacerlo dentro de la consulta y no recorrerlas cada vez, sería genial. -

4 Respuestas

var topicsQuery = from x in board.Topics
                  let lastActivityDate = x.Replies.Any() 
                         ? x.Replies.Last().PostedDate 
                         : x.PostedDate
                  orderby lastActivityDate descending
                  select x;

Edit:

Para responder a su comentario, no hay 'let' para la sintaxis de expresión linq explícitamente. Pero, puede lograr lo mismo de la siguiente manera (usando una expresión de selección intermedia):

var topicsQuery = board.Topics.Select(x => new { 
                                                  Topic = x, 
                                                  LastActivityDate = x.Replies.Any() 
                                                      ? x.Replies.Last().PostedDate 
                                                      : x.PostedDate
                                    })
                             .OrderByDescending(p => p.LastActivityDate)
                             .Select(r => r.Topic)

Edit2:

Esto se puede simplificar aún más, como sugirió Nicholas, también podemos eliminar la declaración de selección intermedia. Tenga en cuenta que esto no habría sido posible si no fuera por una ejecución diferente.

var topicsQuery = board.Topics
        .OrderByDescending(x => x.Replies.Any() 
                                      ? x.Replies.Last().PostedDate 
                                      : x.PostedDate);

Respondido el 09 de enero de 11 a las 08:01

Te amo. me ahorraste un montón de tiempo. No estaba al tanto de .Any () y 'dejar' agradecer +1 y aceptar cuando se acabe el tiempo y dejarme. - Chev

¿Podría proporcionar un ejemplo de cómo usaría la palabra clave let en chain linq? Como dentro .OrderBy(x => x.PostedDate) - Chev

@Chevex: Editó la respuesta para proporcionar un ejemplo. - Mahesh Velaga

De hecho, no es necesaria la cláusula let, el operador ternario es una expresión y se puede pasar a la cláusula orderby LINQ, así como al cuerpo del método lambda OrderByDescending. - Nicolás Buduroi

@Nicolas: Los usé para mostrar el uso (seleccionar y dejar), lo que permite una legibilidad más explícita en el código para las personas que no están acostumbradas a estos. - Mahesh Velaga

No puedo comentar. FWIW, trata esto como un comentario.

var topicsQuery = from x in board.Topics
              let lastActivityDate = x.Replies.Any() 
                     ? x.Replies.Last().PostedDate 
                     : x.PostedDate
              orderby lastActivityDate descending
              select x;

Es posible que desee cambiar

x.Replies.Last().PostedDate 

este

x.Replies.OrderByDesc( r => r.PostedDate).First().PostedDate

Respondido el 09 de enero de 11 a las 08:01

Deberías poder comentar ahora ..;) - Yo no

@Chris, Yee-hooo, aquí voy. - usuario394307

¿Ha pensado en agregar un nuevo campo a sus datos de Temas? Algo como LastActivityDate. Configúrelo en los temas PostedDate inicialmente y luego actualice cada vez que se agregue una respuesta.

No soy un experto en SQL Server, pero si intenta hacer lo que está pidiendo con una consulta, probablemente terminará con una consulta que ignorará sus índices y realizará un escaneo completo de la tabla.

Respondido el 09 de enero de 11 a las 08:01

De hecho, esa es la forma en que solía hacerlo en un sitio antiguo que hacía con consultas SQL en línea. Sentí que era redundante y gracias a la respuesta anterior puedo reemplazarlo. - Chev

Esto también significaría que cuando se diera una respuesta, se actualizaría este nuevo campo de respuesta con el tiempo de respuesta y el tema pasaría a la parte superior de la lista. Pero, ¿qué pasa si el usuario borra esa respuesta? Realmente no habría una manera fácil de volver al tiempo de respuesta anterior. Tendría que leer la última respuesta como lo estoy haciendo ahora. Es mucho mejor dejar el tiempo de respuesta dinámico basado en la última publicación en mi humilde opinión. - Chev

Un foro típico tiene muchas lecturas y pocas escrituras, especialmente en este caso particular. Por lo tanto, elegiría almacenar en caché LastPostDate en un campo adicional. Da algo de redundancia y un poco más de programación para mantener el valor actualizado, pero creo que en la mayoría de los casos es la mejor manera de hacerlo.

Respondido el 09 de enero de 11 a las 09:01

Es cierto, pero es una escritura adicional con cada respuesta. Además, con LINQ y la carga diferida, creo que obtener la última fecha de respuesta es más eficiente. Mismo número de lecturas, una escritura menos. - Chev

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.