Comment pouvez-vous faire une pagination avec NHibernate?


107

Par exemple, je souhaite remplir un contrôle gridview dans une page Web ASP.NET avec uniquement les données nécessaires pour le nombre de lignes affichées. Comment NHibernate peut-il soutenir cela?

Réponses:


111

ICriteriaa une SetFirstResult(int i)méthode, qui indique l'index du premier élément que vous souhaitez obtenir (essentiellement la première ligne de données de votre page).

Il a également une SetMaxResults(int i)méthode, qui indique le nombre de lignes que vous souhaitez obtenir (c'est-à-dire la taille de votre page).

Par exemple, cet objet de critères obtient les 10 premiers résultats de votre grille de données:

criteria.SetFirstResult(0).SetMaxResults(10);

1
C'est à peu près à quoi ressemblerait la syntaxe Linq (to NH) de toute façon - Nice.
MotoWilliams

13
Il est important de noter que vous devrez exécuter une transaction distincte pour récupérer le nombre total de lignes afin de rendre votre pager.
Kevin Pang

1
Cela exécute une requête SELECT TOP dans SQL Server. Essayez-le avec SetFirstResult (1) .SetMaxResult (2);
Chris S

4
Ce commentaire précédent utilise NHibernate.Dialect.MsSql2000Dialect et non NHibernate.Dialect.MsSql2005Dialect
Chris S

IQuery a les mêmes fonctions, il peut donc également être utilisé avec HQL.
goku_da_master

87

Vous pouvez également profiter de la fonctionnalité Futures de NHibernate pour exécuter la requête afin d'obtenir le nombre total d'enregistrements ainsi que les résultats réels dans une seule requête.

Exemple

 // 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>();

Pour obtenir le nombre total d'enregistrements, procédez comme suit:

int iRowCount = rowCount.Value;

Une bonne discussion de ce que les Futures vous offrent est ici .


3
C'est bien. Futures fonctionne exactement comme des multicritères sans la complexité syntaxique des multicritères.
DavGarcia

Après avoir lu l'article sur Futures, je me demande si je devrais utiliser Future pour toutes mes requêtes de base de données ... Quel est l'inconvénient? :)
hakksor

46

À partir de NHibernate 3 et supérieur, vous pouvez utiliser QueryOver<T>:

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

Vous pouvez également ordonner explicitement vos résultats comme ceci:

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

.Skip(PageNumber * PageSize)de cette façon, si la taille de la page est de 10, il ne récupérera jamais les 10 premières lignes. Je modifie pour que la formule soit correcte. En supposant que conceptuellement, cela PageNumberne devrait pas être égal à 0. Il devrait être au minimum 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); }
      }

Lors de la pagination des données, existe-t-il un autre moyen d'obtenir le résultat tapé de MultiCriteria ou tout le monde fait la même chose comme moi?

Merci


23

Pourquoi ne pas utiliser Linq pour NHibernate comme indiqué dans ce billet de blog d'Ayende?

Exemple de code:

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

Et voici un article détaillé du blog de l'équipe NHibernate sur l' accès aux données avec NHibernate, y compris la mise en œuvre de la pagination.


Remarque linq to Nhibernate est dans le package contrib et non inclus dans la version NHibernate 2.0
Richard

11

Très probablement, dans un GridView, vous souhaiterez afficher une tranche de données plus le nombre total de lignes (rowcount) de la quantité totale de données correspondant à votre requête.

Vous devez utiliser une MultiQuery pour envoyer à la fois la requête Select count (*) et .SetFirstResult (n) .SetMaxResult (m) à votre base de données en un seul appel.

Notez que le résultat sera une liste contenant 2 listes, une pour la tranche de données et une pour le décompte.

Exemple:

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];

6

Je vous suggère de créer une structure spécifique pour gérer la pagination. Quelque chose comme (je suis un programmeur Java, mais cela devrait être facile à mapper):

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()

}

Je n'ai pas fourni d'implémentation, mais vous pouvez utiliser les méthodes suggérées par @Jon . Voici une bonne discussion à examiner.


0

Vous n'avez pas besoin de définir 2 critères, vous pouvez en définir un et le cloner. Pour cloner les critères de nHibernate, vous pouvez utiliser un code simple:

var criteria = ... (your criteria initializations)...;
var countCrit = (ICriteria)criteria.Clone();
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.