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?

Rayon
la source

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);
Jon Limjap
la source
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 .

Jérémie D
la source
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();
Leandro de los Santos
la source
.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

Alpage Barbaros
la source
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.

Pas moi
la source
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];
Zadam
la source
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.

Marcio Aguiar
la source
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();
Marcin
la source