¿Cómo se puede realizar la paginación con NHibernate?

107

Por ejemplo, quiero completar un control de vista de cuadrícula en una página web ASP.NET con solo los datos necesarios para el número de filas mostradas. ¿Cómo puede NHibernate apoyar esto?

Rayo
fuente

Respuestas:

111

ICriteriatiene un SetFirstResult(int i)método, que indica el índice del primer elemento que desea obtener (básicamente la primera fila de datos en su página).

También tiene un SetMaxResults(int i)método, que indica el número de filas que desea obtener (es decir, el tamaño de su página).

Por ejemplo, este objeto de criterios obtiene los primeros 10 resultados de su cuadrícula de datos:

criteria.SetFirstResult(0).SetMaxResults(10);
Jon Limjap
fuente
1
Esto es más o menos como se vería la sintaxis de Linq (to NH) de todos modos: agradable.
MotoWilliams
13
Es importante tener en cuenta que deberá ejecutar una transacción separada para recuperar el recuento total de filas con el fin de representar su localizador.
Kevin Pang
1
Esto realiza una consulta SELECT TOP en SQL Server. Pruébelo con SetFirstResult (1) .SetMaxResult (2);
Chris S
4
Ese comentario anterior está usando NHibernate.Dialect.MsSql2000Dialect no NHibernate.Dialect.MsSql2005Dialect
Chris S
IQuery tiene las mismas funciones, por lo que también se puede usar con HQL.
goku_da_master
87

También puede aprovechar la función Futuros en NHibernate para ejecutar la consulta y obtener el recuento total de registros, así como los resultados reales en una sola consulta.

Ejemplo

 // Get the total row count in the database.
var rowCount = this.Session.CreateCriteria(typeof(EventLogEntry))
    .Add(Expression.Between("Timestamp", startDate, endDate))
    .SetProjection(Projections.RowCount()).FutureValue<Int32>();

// Get the actual log entries, respecting the paging.
var results = this.Session.CreateCriteria(typeof(EventLogEntry))
    .Add(Expression.Between("Timestamp", startDate, endDate))
    .SetFirstResult(pageIndex * pageSize)
    .SetMaxResults(pageSize)
    .Future<EventLogEntry>();

Para obtener el recuento total de registros, haga lo siguiente:

int iRowCount = rowCount.Value;

Aquí encontrará una buena discusión sobre lo que le ofrecen los futuros .

Jeremy D
fuente
3
Esto es genial. Futures funciona exactamente igual que los multicriterios sin la complejidad sintáctica de los multicriterios.
DavGarcia
Después de leer la publicación sobre Futures, me pregunto si debería usar Future para todas mis consultas de base de datos ... ¿Cuál es el inconveniente? :)
hakksor
46

Desde NHibernate 3 y superior, puede usar QueryOver<T>:

var pageRecords = nhSession.QueryOver<TEntity>()
            .Skip((PageNumber - 1) * PageSize)
            .Take(PageSize)
            .List();

Es posible que también desee ordenar explícitamente sus resultados de esta manera:

var pageRecords = nhSession.QueryOver<TEntity>()
            .OrderBy(t => t.AnOrderFieldLikeDate).Desc
            .Skip((PageNumber - 1) * PageSize)
            .Take(PageSize)
            .List();
Leandro de los Santos
fuente
.Skip(PageNumber * PageSize)de esta manera, si el tamaño de la página es 10, nunca recuperará las primeras 10 filas. Estoy editando para que la fórmula sea correcta. Suponiendo que conceptualmente, PageNumberno debería ser 0. Debería ser mínimo 1.
Amit Joshi
31
public IList<Customer> GetPagedData(int page, int pageSize, out long count)
        {
            try
            {
                var all = new List<Customer>();

                ISession s = NHibernateHttpModule.CurrentSession;
                IList results = s.CreateMultiCriteria()
                                    .Add(s.CreateCriteria(typeof(Customer)).SetFirstResult(page * pageSize).SetMaxResults(pageSize))
                                    .Add(s.CreateCriteria(typeof(Customer)).SetProjection(Projections.RowCountInt64()))
                                    .List();

                foreach (var o in (IList)results[0])
                    all.Add((Customer)o);

                count = (long)((IList)results[1])[0];
                return all;
            }
            catch (Exception ex) { throw new Exception("GetPagedData Customer da hata", ex); }
      }

Cuando los datos de paginación, ¿hay otra forma de obtener un resultado escrito de MultiCriteria o todos hacen lo mismo como yo?

Gracias

Barbaros Alp
fuente
23

¿Qué tal usar Linq para NHibernate como se explica en esta publicación de blog de Ayende?

Ejemplo de código:

(from c in nwnd.Customers select c.CustomerID)
        .Skip(10).Take(10).ToList(); 

Y aquí hay una publicación detallada del blog del equipo de NHibernate sobre Acceso a datos con NHibernate, incluida la implementación de paginación.

No a mí mismo
fuente
Tenga en cuenta que linq to Nhibernate está en el paquete contrib y no está incluido en la versión de NHibernate 2.0
Richard
11

Lo más probable es que en un GridView desee mostrar una porción de datos más el número total de filas (recuento de filas) de la cantidad total de datos que coinciden con su consulta.

Debe usar una MultiQuery para enviar tanto la consulta Select count (*) como las consultas .SetFirstResult (n) .SetMaxResult (m) a su base de datos en una sola llamada.

Tenga en cuenta que el resultado será una lista que contiene 2 listas, una para el segmento de datos y otra para el recuento.

Ejemplo:

IMultiQuery multiQuery = s.CreateMultiQuery()
    .Add(s.CreateQuery("from Item i where i.Id > ?")
            .SetInt32(0, 50).SetFirstResult(10))
    .Add(s.CreateQuery("select count(*) from Item i where i.Id > ?")
            .SetInt32(0, 50));
IList results = multiQuery.List();
IList items = (IList)results[0];
long count = (long)((IList)results[1])[0];
zadam
fuente
6

Sugiero que cree una estructura específica para lidiar con la paginación. Algo como (soy un programador de Java, pero debería ser fácil de mapear):

public class Page {

   private List results;
   private int pageSize;
   private int page;

   public Page(Query query, int page, int pageSize) {

       this.page = page;
       this.pageSize = pageSize;
       results = query.setFirstResult(page * pageSize)
           .setMaxResults(pageSize+1)
           .list();

   }

   public List getNextPage()

   public List getPreviousPage()

   public int getPageCount()

   public int getCurrentPage()

   public void setPageSize()

}

No proporcioné una implementación, pero podrías usar los métodos sugeridos por @Jon . Aquí tienes una buena discusión para que la eches un vistazo.

Marcio Aguiar
fuente
0

No necesita definir 2 criterios, puede definir uno y clonarlo. Para clonar los criterios de nHibernate, puede usar un código simple:

var criteria = ... (your criteria initializations)...;
var countCrit = (ICriteria)criteria.Clone();
Marcin
fuente