Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

NHibernate 3.0: Using ICriteria and Paged Queries in the Data Access Layer

Save for later
  • 4 min read
  • 21 Oct 2010

article-image

NHibernate 3.0 Cookbook


Get solutions to common NHibernate problems to develop high-quality performance-critical data access applications

  • Master the full range of NHibernate features
  • Reduce hours of application development time and get better application architecture and performance
  • Create, maintain, and update your database structure automatically with the help of NHibernate
  • Written and tested for NHibernate 3.0 with input from the development team distilled in to easily accessible concepts and examples
  • Part of Packt's Cookbook series: each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible

Using ICriteria in the data access layer


For queries where the criteria are not known in advance, such as a website's advanced product search, ICriteria queries are more appropriate than named HQL queries. This article by Jason Dentler, author of NHibernate 3.0 Cookbook, shows how to use the same DAL infrastructure with ICriteria and QueryOver queries.

In an effort to avoid overwhelming the user, and increase application responsiveness, large result sets are commonly broken into smaller pages of results. This article also shows how we can easily add paging to a QueryOver query object in our DAL.

Getting ready


Complete the previous recipe, Using Named Queries in the data access layer.

How to do it...

  1. In Eg.Core.Data.Impl.Queries, add a new, empty, public interface named ICriteriaQuery.
  2. Add a class named CriteriaQueryBase with the following code:

    public abstract class CriteriaQueryBase<TResult> :
    NHibernateQueryBase<TResult>, ICriteriaQuery
    {


    public CriteriaQueryBase(ISessionFactory sessionFactory)
    : base(sessionFactory) { }

    public override TResult Execute()
    {
    var criteria = GetCriteria();
    return Transact(() => Execute(criteria));
    }

    protected abstract ICriteria GetCriteria();

    protected abstract TResult Execute(ICriteria criteria);
    }

    
    

  3. In Eg.Core.Data.Queries, add the following enum:

    public enum AdvancedProductSearchSort
    {
    PriceAsc,
    PriceDesc,
    Name
    }


    Unlock access to the largest independent learning library in Tech for FREE!
    Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
    Renews at $15.99/month. Cancel anytime
    
    

  4. Add a new interface named IAdvancedProductSearch with the following code:

    public interface IAdvancedProductSearch
    : IQuery<IEnumerable<Product>>
    {
    string Name { get; set; }
    string Description { get; set; }
    decimal? MinimumPrice { get; set; }
    decimal? MaximumPrice { get; set; }
    AdvancedProductSearchSort Sort { get; set; }
    }


    
    

  5. In Eg.Core.Data.Impl.Queries, add the following class:

    public class AdvancedProductSearch
    : CriteriaQueryBase<IEnumerable<Product>>,
    IAdvancedProductSearch
    {


    public AdvancedProductSearch(ISessionFactory sessionFactory)
    : base(sessionFactory) { }

    public string Name { get; set; }

    public string Description { get; set; }

    public decimal? MinimumPrice { get; set; }

    public decimal? MaximumPrice { get; set; }

    public AdvancedProductSearchSort
    Sort { get; set; }

    protected override ICriteria GetCriteria()
    {
    return GetProductQuery().UnderlyingCriteria;
    }

    protected override IEnumerable<Product> Execute(
    ICriteria criteria)
    {
    return criteria.List<Product>();
    }

    private IQueryOver GetProductQuery()
    {
    var query = session.QueryOver<Product>();
    AddProductCriterion(query);
    return query;
    }

    private void AddProductCriterion<T>(
    IQueryOver<T, T> query) where T : Product
    {
    if (!string.IsNullOrEmpty(Name))
    query = query.WhereRestrictionOn(p => p.Name)
    .IsInsensitiveLike(Name, MatchMode.Anywhere);

    if (!string.IsNullOrEmpty(Description))
    query.WhereRestrictionOn(p => p.Description)
    .IsInsensitiveLike(Description, MatchMode.Anywhere);

    if (MinimumPrice.HasValue)
    query.Where(p => p.UnitPrice >= MinimumPrice);

    if (MaximumPrice.HasValue)
    query.Where(p => p.UnitPrice <= MaximumPrice);

    switch (Sort)
    {
    case AdvancedProductSearchSort.PriceDesc:
    query = query.OrderBy(p => p.UnitPrice).Desc;
    break;
    case AdvancedProductSearchSort.Name:
    query = query.OrderBy(p => p.Name).Asc;
    break;
    default:
    query = query.OrderBy(p => p.UnitPrice).Asc;
    break;
    }
    }
    }

    
    

How it works...


In this recipe, we reuse the same repository and query infrastructure from the Using Named Queries in The Data Access Layer recipe. Our simple base class for ICriteria-based query objects splits query creation from query execution and handles transactions for us automatically.

The example query we use is typical for an "advanced product search" use case. When a user fills in a particular field on the UI, the corresponding criterion is included in the query. When the user leaves the field blank, we ignore it.

We check each search parameter for data. If the parameter has data, we add the appropriate criterion to the query. Finally, we set the order by clause based on the Sort parameter and return the completed ICriteria query. The query is executed inside a transaction, and the results are returned.

There's more...


For this type of query, typically, each query parameter would be set to the value of some field on your product search UI. On using this query, your code looks like this:

var query = repository.CreateQuery<IAdvancedProductSearch>();
query.Name = searchCriteria.PartialName;
query.Description = searchCriteria.PartialDescription;
query.MinimumPrice = searchCriteria.MinimumPrice;
query.MaximumPrice = searchCriteria.MaximumPrice;
query.Sort = searchCriteria.Sort;
var results = query.Execute();