2009-06-26 48 views
30

En marco de la entidad, utilizando LINQ a Entidades, la base de datos de localización se realiza generalmente en forma siguiente:de paginación en Entity Framework

int totalRecords = EntityContext.Context.UserSet.Count; 
var list  = EntityContext.Context.UserSet 
       .Skip(startingRecordNumber) 
       .Take(pageSize) 
       .ToList(); 

Esto se traduce en DOS llamadas bases de datos.

Dígale, cómo reducirlo a UNA llamada a la base de datos.

Gracias.

+6

En EF resulta en un error, debe llamar a OrderBy antes de llamar Skip :) Sería bueno si actualiza su código. Alguien podría haber perdido mucho tiempo copiando el código de la publicación. –

+0

El truco de cómo hacerlo está en http://stackoverflow.com/questions/7767409/better-way-to-query-a-page-of-data-and-get-total-count-in-entity-framework -4-1, pero es mejor tener un diseño simple con 2 llamadas –

Respuesta

4

Hmmm ... la llamada real que usa paginación es la segunda, es una sola llamada.

La segunda llamada es para determinar el número total de filas; esa es una operación bastante diferente, y no conozco ninguna forma de combinar esas dos operaciones distintas en una única llamada a la base de datos con Entity Framework.

Pregunta es: ¿usted realmente necesita el número total de filas? ¿Para qué? ¿Vale la pena una segunda llamada a la base de datos o no?

Otra opción que tendría es usar EntityObjectSource (en ASP.NET) y luego vincular esto a, p. Ej. un GridView y habilite AllowPaging y AllowSorting, etc. en GridView, y permita que el tiempo de ejecución de ASP.NET maneje todo el trabajo esencial de recuperar la página de datos apropiada y mostrarla.

Marc

+3

Necesita el total de registros para saber cuántas páginas tiene en su interfaz paginada. Me pregunto si podrías hacer una lista. ¿Custodio o algo así? – rball

+0

Bueno, el List.Count probablemente buscará todas las filas de la base de datos, eso definitivamente no es algo que desee. Además, estoy bastante seguro de que Linq to Entities te devolverá un conjunto vacío de preguntas por una página más allá de su conjunto de datos real, así que, de nuevo: ¿por qué necesitas el número total de filas? De Verdad? Seguro que es bueno mostrar "Página 5 de 17" en su página, ¿puede vivir sin él? –

+0

Gracias por las respuestas. UI requiere una barra de buscapersonas, por lo que se necesita un recuento total de registros. – dev

7

Usando esql y el mapeo de un procedimiento almacenado a una entidad puede resolver el problema. SP devolverá totalRows como parámetro de salida y página actual como conjunto de resultados.

CREATE PROCEDURE getPagedList(
@PageNumber int, 
@PageSize int, 
@totalRecordCount int OUTPUT 
AS 

//Return paged records 

Por favor, asesorar.

Gracias.

+0

+1 Agradable. Eso te daría 1 llamada a la base de datos. Aún estarías ejecutando dos consultas, pero serán rápidas y pequeñas. –

33

¿Qué pasa con dos llamadas? Son consultas pequeñas y rápidas. Las bases de datos están diseñadas para admitir muchas consultas pequeñas.

Desarrollar una solución compleja para hacer una consulta de búsqueda no le dará mucho beneficio.

3
ALTER proc [dbo].[GetNames] 
    @lastRow bigint, 
    @pageSize bigint, 
    @totalRowCount bigint output 
as 
begin 

select @totalRowCount = count(*) from _firstNames, _lastNames 

select 
    FirstName, 
    LastName, 
    RowNumber 
from 
(
    select 
     fn.[FirstName] as FirstName, 
     ln.[Name] as LastName, 
     row_number() over(order by FirstName) as RowNumber 
    from 
     _firstNames fn, _lastNames ln 
) as data 
where 
    RowNumber between (@lastRow + 1) and (@lastRow + @pageSize) 

end 

No hay manera de conseguir esto en una llamada, pero esto funciona lo suficientemente rápido.

+4

En realidad es posible. Consulte esta respuesta para saber cómo: http://stackoverflow.com/a/7771298/1131804 –

-1

Suponga que desea obtener los detalles de la página 2, con un tamaño de página = 4

int page =2; 
int pagesize=4; 

var pagedDetails= Categories.Skip(pagesize*(page-1)).Take(pagesize) 
.Join(Categories.Select(item=>new {item.CategoryID,Total = Categories.Count()}),x=>x.CategoryID,y=>y.CategoryID,(x,y)=>new {Category = x,TotalRows=y.Total}); 

la salida tendrá todos los detalles de categoría y totalRows.

One DB call.

SQL generado

-- Region Parameters 
DECLARE @p0 Int = 2 
DECLARE @p1 Int = 4 
-- EndRegion 
SELECT [t2].[CategoryID], [t2].[CategoryName], [t2].[Description], [t2].[Picture], [t5].[value] AS [TotalRows] 
FROM (
    SELECT [t1].[CategoryID], [t1].[CategoryName], [t1].[Description], [t1].[Picture], [t1].[ROW_NUMBER] 
    FROM (
     SELECT ROW_NUMBER() OVER (ORDER BY [t0].[CategoryID], [t0].[CategoryName]) AS [ROW_NUMBER], [t0].[CategoryID], [t0].[CategoryName], [t0].[Description], [t0].[Picture] 
     FROM [Categories] AS [t0] 
     ) AS [t1] 
    WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1 
    ) AS [t2] 
INNER JOIN (
    SELECT [t3].[CategoryID], (
     SELECT COUNT(*) 
     FROM [Categories] AS [t4] 
     ) AS [value] 
    FROM [Categories] AS [t3] 
    ) AS [t5] ON [t2].[CategoryID] = [t5].[CategoryID] 
ORDER BY [t2].[ROW_NUMBER] 
+0

Incorrecto. Siempre cuenta todas las categorías. Incluso si se aplica un where en la consulta principal. Además, no devuelve entidades de Categoría sino de tipo anónimo, y cuenta una y otra vez para cada categoría. –

+0

El requisito es: 1- Obtenga los detalles de una página con un tamaño de página 2- Asegúrese de recuperar toda la información en una llamada de DB. Espero que entiendas de lo que estás hablando ... ¿Has visto los requisitos anteriores? ¿Has probado la solución provista por mí o estás compartiendo tus pensamientos solo con mirar? Si lo intenta, será una llamada a DB ... intente y comente. Por supuesto, la entidad de categoría no tiene una propiedad de recuento total, pero aún así queremos volver a la misma llamada ... El tipo de devolución es una clase anónima con propiedades de hormigón Categoría y TotalRows. – Satchi

+0

Una * pequeña * pregunta. ¿Qué sucede si quieren 'Categories.Where (c => c.Name.Contains (" a "))'? –

0

Este consultas son demasiado pequeños para DBManager y no puedo entender por qué usted quiere hacer esto, de todos modos para reducir a uno el uso de llamadas de base de datos siguiente:

var list  = EntityContext.Context.UserSet 
       .Skip(startingRecordNumber) 
       .Take(pageSize) 
       .ToList(); 
int totalRecords = list.Count;