We've learned to use MultiCriteria and MultiQuery to batch our queries together. NHibernate's Futures feature provides a simpler API for batching criteria and queries. In this recipe, I'll show you how to use NHibernate's new Futures feature to return a paged product result.
PagingResults
.CountAllProducts.hbm.xml
with the following xml code. Don't forget to set the Build action to Embedded Resource.<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <query name="CountAllProducts"> <![CDATA[ select count(p.Id) from Product p ]]> </query> </hibernate-mapping>
GetAllProducts.hbm.xml
and set the Build action to embedded resource. Use the following xml:<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <query name="GetAllProducts"> <![CDATA[ from Product p order by p.UnitPrice asc ]]> </query> </hibernate-mapping>
App.config
, add <mapping assembly="PagedResults"/>
below the mapping element for Eg.Core
.Queries
class:public struct PageOf<T> { public int PageCount; public int PageNumber; public IEnumerable<T> PageOfResults; }
Queries
class:public PageOf<Product> GetPageOfProducts( int pageNumber, int pageSize) { var skip = (pageNumber - 1) * pageSize; var productCount = _session.GetNamedQuery("CountAllProducts") .FutureValue<long>(); var products = _session.GetNamedQuery("GetAllProducts") .SetFirstResult(skip) .SetMaxResults(pageSize) .Future<Product>(); var pageCount = (int) Math.Ceiling( productCount.Value/(double) pageSize); return new PageOf<Product>() { PageCount = pageCount, PageOfResults = products, PageNumber = pageNumber }; }
Program.cs
, use the following code in the RunQueries
method:static void RunQueries(ISession session) { var queries = new Queries(session); var result = queries.GetPageOfProducts(1, 2); var heading = string.Format("Page {0} of {1}", result.PageNumber, result.PageCount); Show(heading, result.PageOfResults); }
In this recipe, we are using the simpler Futures syntax to again retrieve a count of all products, along with a page of products for display.
When we call IQuery
or ICriteria
's Future
or FutureValue
, NHibernate returns an object representing the potential results of that query. It also queues up the query in a hidden MultiCriteria
or MultiQuery
inside the session.
When we call FutureValue
, it returns an IFutureValue<>
, representing a single entity or scalar value. For Future
, it returns an IEnumerable<>
. NHibernate waits until we access Value
property of IFutureValue<>
or enumerate the IEnumerable
. When we do, NHibernate executes the hidden futures MultiCriteria
and MultiQuery
for this session.
In this specific example, both queries are executed when we use productCount.Value
to calculate the page count. As you can see, this delayed loading is mostly transparent to the application.
The two minor caveats when using futures are as follows: