2011-07-20 11 views
11

He leído en algún lugar (no recuerdo dónde y cómo) que NHibernate 3 permite la determinación del número total de registros mientras se realiza una consulta paginada (en una consulta de base de datos). ¿Es esto correcto?NHibernate 3 paginación y determinación del número total de filas

tengo este código:

public IEnumerable<X> GetOrganisms(int PageSize, int Page, out int total) 
{ 
    var query = (from e in Session.Query<X>() select e).AsQueryable(); 

    return query.Skip((Page - 1) * PageSize).Take(PageSize).ToList(); 
} 

y me gustaría inicializar 'total' tan eficientemente como sea posible.

Gracias.

Cristiano

PS:

Potencial 'solución' ?:

Total = (int) Session.CreateCriteria<X>() 
.SetProjection(Projections.RowCount()) 
.FutureValue<Int32>().Value; 

var query = (from e in Session.Query<X>() select e).AsQueryable(); 

return query.Skip((Page - 1) * PageSize).Take(PageSize).ToList(); 

Respuesta

23

su posible solución se manipula en una transacción, pero serán dos llamadas a DB. Si debe tener solo una llamada de base de datos, debe usar una consulta multiquery/future como sugerencia de pares. Para obtener más información sobre la sintaxis futura, consulte esta publicación: http://ayende.com/blog/3979/nhibernate-futures.

Estas son algunas maneras de lograr su escenario ... (llamadas 2 db)

QueryOver:
var query = session.QueryOver<Organism>(); 
var result = query 
    .Skip((Page - 1) * PageSize) 
    .Take(PageSize) 
    .List(); 
var rowcount = query.RowCount(); 

Con un conjunto de muestras de 100 organismos, y las consultas a los organismos 11-20 , aquí están las dos consultas enviadas a la db:

SELECT TOP (@p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > @p1 ORDER BY query.__hibernate_sort_row;@p0 = 10 [Type: Int32 (0)], @p1 = 10 [Type: Int32 (0)] 
SELECT count(*) as y0_ FROM Organism this_ 
QueryOver (1 llamada db con futuro):
var query = session.QueryOver<Organism>() 
    .Skip((Page - 1) * PageSize) 
    .Take(PageSize) 
    .Future<Organism>(); 
var result = query.ToList(); 
var rowcount = session.QueryOver<Organism>() 
    .Select(Projections.Count(Projections.Id())) 
    .FutureValue<int>().Value; 

Consulta para el mismo conjunto de datos como antes, esta es la consulta que se genera:

SELECT TOP (@p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > @p1 ORDER BY query.__hibernate_sort_row;SELECT count(this_.Id) as y0_ FROM Organism this_;;@p0 = 10 [Type: Int32 (0)], @p1 = 10 [Type: Int32 (0)] 
Criteria (1 llamada db con futuro):
var criteria = session.CreateCriteria<Organism>() 
    .SetFirstResult((Page - 1) * PageSize) 
    .SetMaxResults(PageSize) 
    .Future<Organism>(); 
var countCriteria = session.CreateCriteria<Organism>() 
    .SetProjection(Projections.Count(Projections.Id())) 
    .FutureValue<int>().Value; 

Una vez más, la consulta para la mismo conjunto de datos, criterios con resultados futuros en la misma consulta:

SELECT TOP (@p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > @p1 ORDER BY query.__hibernate_sort_row;SELECT count(this_.Id) as y0_ FROM Organism this_;;@p0 = 10 [Type: Int32 (0)], @p1 = 10 [Type: Int32 (0)] 

Tenga en cuenta que todos los tres Los estilos de consulta e resultan en las mismas consultas exactas. La sintaxis futura simplemente permite a NHibernate hacer una llamada a la base de datos en lugar de dos.

Si está utilizando NHibernate 3, creo que la manera más elegante de manejar esto es utilizar la nueva sintaxis de QueryOver. (Usó la sintaxis NHibernate.Linq anterior en su solución propuesta. Le conviene más aprender la sintaxis QueryOver.)

+1

Probablemente vale la pena señalar que si los dos resultados en una sola consulta es impulsada por optimizaciones específicas del dialecto. Por ejemplo, en SQLite se emiten dos consultas, pero el comportamiento es el mismo (todos los futuros se ejecutan a la vez en tan pocas consultas como sea posible) – AlexCuse

2

No creo nhibernate 'se da cuenta' el significado de cualquier consulta que realiza, por lo que determinar el número total de las filas no son consultas estándar.

La forma más eficiente para obtener el número de filas es con futuros o un IMultiQuery (para obtener todos los resultados en una roundtript a la base de datos)

nhibernate-futures

+0

He agregado algo, vea PS. ¿Es esto a lo que se refiere y se ejecutará esta consulta en un db trans? Gracias. – cs0815

3

no tengo la reputación suficiente para comentar sobre la solución de CodeProgression arriba ... pero el correcto llamar a uno de DB usando QueryOver w/Futuro <> es:

var query = session.QueryOver<Organism>() 
    .Skip((Page - 1) * PageSize) 
    .Take(PageSize) 
    .Future<Organism>(); 
// var result = query.ToList(); 
var rowcount = session.QueryOver<Organism>() 
    .Select(Projections.Count(Projections.Id())) 
    .FutureValue<int>().Value; 
var result = query.ToList(); 
int iRowCount = rowcount.Value(); 

Una vez que ejecute el .ToList () - Golpeará la base de datos. Así que tendrías que volver a acceder a la base de datos para obtener el rowCount ... lo que frustra el propósito de Future <>. Haga su ToList() DESPUÉS de haber realizado todas sus consultas .Future <>.

Cuestiones relacionadas