41

Actualmente, cuando se necesita para ejecutar una consulta que será utilizado w/paginación lo hago algo como esto:¿Mejor manera de consultar una página de datos y obtener un recuento total en el marco de la entidad 4.1?

//Setup query (Typically much more complex) 
var q = ctx.People.Where(p=>p.Name.StartsWith("A")); 

//Get total result count prior to sorting 
int total = q.Count();  

//Apply sort to query 
q = q.OrderBy(p => p.Name); 

q.Select(p => new PersonResult 
{ 
    Name = p.Name 
}.Skip(skipRows).Take(pageSize).ToArray(); 

Esto funciona, pero me preguntaba si es posible mejorar esto sea más eficiente al mismo tiempo usando linq? No se me ocurrió una forma de combinar el recuento con la recuperación de datos en un solo viaje al DB sin usar un proceso almacenado.

+1

Eche un vistazo a [¿Cómo debo exponer el recuento total de registros y la colección IEnumable de registros paginados de mi método de capa de servicio?] (Http://stackoverflow.com/questions/6417886/how-should-i-expose- the-total-record-count-and-ienumable-collection-of-paged-reco/6418761 # 6418761) – Eranga

+0

Interesante, pero parece ser lo mismo que lo que estoy haciendo. Hace 2 llamadas distintas a la base de datos. Uno para el recuento total y otro para la página de datos. –

+3

EF no tiene consultas futuras como nHibernate. Tomará 2 viajes a la base de datos. – Eranga

Respuesta

65

La siguiente consulta obtendrá el recuento y los resultados de la página en un solo viaje a la base de datos, pero si comprueba el SQL en LINQPad, verá que no es muy bonito. Solo puedo imaginar lo que sería para una consulta más compleja.

var query = ctx.People.Where (p => p.Name.StartsWith("A")); 

var page = query.OrderBy (p => p.Name) 
       .Select (p => new PersonResult { Name = p.Name })   
       .Skip(skipRows).Take(pageSize) 
       .GroupBy (p => new { Total = query.Count() }) 
       .First(); 

int total = page.Key.Total; 
var people = page.Select(p => p); 

Para una consulta simple como esto, que probablemente se podría utilizar cualquiera de los métodos (2 viajes a la base de datos, o el uso de GroupBy hacerlo en 1 viaje) y no notar mucha diferencia. Para cualquier cosa compleja, creo que un procedimiento almacenado sería la mejor solución.

+4

+1, ¡Bonito! No pensé que fuera posible en absoluto. – Slauma

+12

Lanzará un error si la tabla no tiene registros. Para resolverlo, simplemente reemplace .First() con .FirstOrDefault() y recuerde comprobar si el resultado no es nulo. –

+0

También tengo el mismo problema porque actualmente uso DAPPER y tiene múltiples opciones de consulta para recuperar múltiples consultas en una sola llamada. La solución de eliminación es admirable, ya que creo que no era posible en EF. muchas gracias a la deriva. – Prageeth

2

Sugiero hacer dos consultas para la primera página, una para el recuento total y otra para la primera página o resultados.

Almacena en caché el recuento total para usarlo a medida que avanzas más allá de la primera página.

+2

Almacenar en caché el total puede causar incoherencia, si cambia el número de registros entre la primera y las siguientes llamadas a la página –

+1

, pero a menudo no importa, especialmente si hay muchas edades de resultados. Cuando necesitaba el recuento y los resultados en conjunto, una sola consulta era demasiado lenta y difícil de leer en comparación con dos consultas. – Bryan

+0

Solo asegúrese de que su recuento total en caché esté guardado en caché específicamente para cualquier cláusula where. Si su primera consulta es 'ctx.People.Where (p => p.Name.StartsWith (" A "))', no desea reutilizar el conteo total en la siguiente consulta 'ctx.People.Where (p => p.Name.StartsWith ("B")) ' – xr280xr

Cuestiones relacionadas