Como você pode fazer paginação com NHibernate?

107

Por exemplo, desejo preencher um controle gridview em uma página da web ASP.NET apenas com os dados necessários para o número de linhas exibidas. Como o NHibernate pode suportar isso?

Raio
fonte

Respostas:

111

ICriteriatem um SetFirstResult(int i)método, que indica o índice do primeiro item que você deseja obter (basicamente, a primeira linha de dados em sua página).

Ele também possui um SetMaxResults(int i)método, que indica o número de linhas que você deseja obter (ou seja, o tamanho da página).

Por exemplo, este objeto de critérios obtém os primeiros 10 resultados de sua grade de dados:

criteria.SetFirstResult(0).SetMaxResults(10);
Jon Limjap
fonte
1
Isso é basicamente o que a sintaxe Linq (para NH) pareceria de qualquer maneira - Legal.
MotoWilliams de
13
É importante observar que você precisará executar uma transação separada para recuperar a contagem total de linhas a fim de renderizar seu pager.
Kevin Pang
1
Isso executa uma consulta SELECT TOP no SQL Server. Experimente com SetFirstResult (1) .SetMaxResult (2);
Chris S
4
Esse comentário anterior está usando NHibernate.Dialect.MsSql2000Dialect, não NHibernate.Dialect.MsSql2005Dialect
Chris S
IQuery tem as mesmas funções, portanto, também pode ser usado com HQL.
goku_da_master
87

Você também pode tirar proveito do recurso Futures no NHibernate para executar a consulta para obter a contagem total de registros, bem como os resultados reais em uma única consulta.

Exemplo

 // 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 obter a contagem total de registros, você faz o seguinte:

int iRowCount = rowCount.Value;

Uma boa discussão sobre o que o Futures oferece a você está aqui .

Jeremy D
fonte
3
Isso é ótimo. Futures funciona exatamente como multicritério sem a complexidade sintática de multicritério.
DavGarcia
Depois de ler o post sobre Futures, fico pensando se devo usar Future para todas as minhas consultas de banco de dados ... Qual é a desvantagem? :)
hakksor
46

Do NHibernate 3 e superior, você pode usar QueryOver<T>:

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

Você também pode querer ordenar explicitamente seus resultados desta forma:

var pageRecords = nhSession.QueryOver<TEntity>()
            .OrderBy(t => t.AnOrderFieldLikeDate).Desc
            .Skip((PageNumber - 1) * PageSize)
            .Take(PageSize)
            .List();
Leandro de los Santos
fonte
.Skip(PageNumber * PageSize)dessa forma, se o tamanho da página for 10, ele nunca recuperará as primeiras 10 linhas. Estou editando para tornar a fórmula correta. Supondo que conceitualmente, PageNumbernão deve ser 0. Deve 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); }
      }

Quando dados de paginação, há outra maneira de obter resultados digitados de MultiCriteria ou todos fazem o mesmo como eu?

obrigado

Barbaros Alp
fonte
23

Que tal usar o Linq para NHibernate, conforme discutido neste post de blog de Ayende?

Amostra de código:

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

E aqui está uma postagem detalhada do blog da equipe do NHibernate sobre Acesso a dados com NHibernate, incluindo a implementação de paginação.

Eu não
fonte
Observe que o linq para Nhibernate está no pacote contrib e não está incluído na versão 2.0 do NHibernate
Richard,
11

Muito provavelmente, em um GridView, você desejará mostrar uma fatia de dados mais o número total de linhas (número de linhas) da quantidade total de dados que correspondem à sua consulta.

Você deve usar uma MultiQuery para enviar a consulta Select count (*) e as consultas .SetFirstResult (n) .SetMaxResult (m) ao seu banco de dados em uma única chamada.

Observe que o resultado será uma lista que contém 2 listas, uma para a fatia de dados e outra para a contagem.

Exemplo:

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
fonte
6

Eu sugiro que você crie uma estrutura específica para lidar com a paginação. Algo como (sou um programador Java, mas deve 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()

}

Não forneci uma implementação, mas você pode usar os métodos sugeridos por @Jon . Aqui está uma boa discussão para você dar uma olhada.

Marcio aguiar
fonte
0

Você não precisa definir 2 critérios, você pode definir um e cloná-lo. Para clonar os critérios do nHibernate, você pode usar um código simples:

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