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, we'll show you how to use NHibernate's new Futures feature to return a paged product result. Both LINQ and HQL are used in the same method, to show the syntactic difference but also that they can work together.
Complete the Getting Ready instructions at the beginning of Chapter 4, Queries.
Futures
to the QueryRecipes
project.struct
named PageOf
to the folder:using System.Collections.Generic; namespace QueryRecipes.Futures { public struct PageOf<T> { public int PageCount; public int PageNumber; public IEnumerable<T> PageOfResults; } }
Queries
to the folder:using System; using System.Linq; using NH4CookbookHelpers.Queries.Model; using NHibernate; using NHibernate.Linq; namespace QueryRecipes.Futures { public class Queries { private readonly ISession _session; public Queries(ISession session) { _session = session; } public PageOf<Product> GetPageOfProducts( int pageNumber, int pageSize) { var skip = (pageNumber - 1) * pageSize; var productCount = _session.Query<Product>() .ToFutureValue(x=>x.Count()); var products = GetPageQuery(skip, pageSize) .Future<Product>(); var pageCount = (int)Math.Ceiling( productCount.Value / (double)pageSize); return new PageOf<Product>() { PageCount = pageCount, PageOfResults = products, PageNumber = pageNumber }; } private IQuery GetPageQuery(int skip, int take) { var hql = @"from Product p order by p.UnitPrice asc"; return _session.CreateQuery(hql) .SetFirstResult(skip) .SetMaxResults(take); } } }
Recipe
to the folder:using NH4CookbookHelpers.Queries; using NHibernate; namespace QueryRecipes.Futures { public class Recipe : QueryRecipe { protected override void Run(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); } } }
Futures
recipe.In this recipe, we will use the Futures
syntax to retrieve a count of all the products, along with a page of products for display.
When we call IQuery
, ICriteria
instance 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. The same thing happens for a LINQ query when ToFuture
or ToFutureValue
is used. The naming difference is purely a matter of following LINQ's naming conventions.
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 the Value
property of IFutureValue<>
or enumerate the IEnumerable
. When we do, NHibernate executes all the postponed Futures
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 deferred loading is mostly transparent to the application.
The two minor caveats while using Futures are as follows:
ICriteria
and IQuery
objects are handled separately in the session. If you have an ICriteria
and an IQuery
based Future, evaluating one will not execute the other.3.137.220.92