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 named queries in the data access layer

Save for later
  • 4 min read
  • 15 Oct 2010

article-image

Getting ready


Download the latest release of the Common Service Locator from http://commonservicelocator.codeplex.com, and extract Microsoft.Practices.ServiceLocation.dll to your solution's libs folder.

Complete the previous recipe, Setting up an NHibernate repository.

Following the Fast testing with SQLite in-memory database recipe in the previous article, create a new NHibernate test project named Eg.Core.Data.Impl.Test.

Include the Eg.Core.Data.Impl assembly as an additional mapping assembly in your test project's App.Config with the following xml:

<mapping assembly="Eg.Core.Data.Impl"/>



How to do it...

  1. In the Eg.Core.Data project, add a folder for the Queries namespace.
  2. Add the following IQuery interfaces:

    public interface IQuery
    {
    }
    public interface IQuery<TResult> : IQuery
    {
    TResult Execute();
    }


    
    

  3. Add the following IQueryFactory interface:

    {
    TQuery CreateQuery<TQuery>() where TQuery :IQuery;
    }


    
    

  4. Change the IRepository interface to implement the IQueryFactory interface, as shown in the following code:

    public interface IRepository<T>
    : IEnumerable<T>, IQueryFactory
    where T : Entity
    {
    void Add(T item);
    bool Contains(T item);
    int Count { get; }
    bool Remove(T item);
    }


    
    

  5. In the Eg.Core.Data.Impl project, change the NHibernateRepository constructor and add the _queryFactory field, as shown in the following code:

    private readonly IQueryFactory _queryFactory;


    public NHibernateRepository(ISessionFactory sessionFactory,
    IQueryFactory queryFactory)
    : base(sessionFactory)
    {
    _queryFactory = queryFactory;
    }

    
    

  6. Add the following method to NHibernateRepository:

    public TQuery CreateQuery<TQuery>() where TQuery : IQuery
    {
    return _queryFactory.CreateQuery<TQuery>();
    }


    
    

  7. In the Eg.Core.Data.Impl project, add a folder for the Queries namespace.
  8. To the Eg.Core.Data.Impl project, add a reference to Microsoft.Practices.ServiceLocation.dll.
  9. To the Queries namespace, add this QueryFactory class:

    public class QueryFactory : IQueryFactory
    {


    private readonly IServiceLocator _serviceLocator;

    public QueryFactory(IServiceLocator serviceLocator)
    {
    _serviceLocator = serviceLocator;
    }

    public TQuery CreateQuery<TQuery>() where TQuery : IQuery
    {
    return _serviceLocator.GetInstance<TQuery>();
    }
    }

    
    

    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 ₹800/month. Cancel anytime
  10. Add the following NHibernateQueryBase class:

    public abstract class NHibernateQueryBase<TResult>
    : NHibernateBase, IQuery<TResult>
    {
    protected NHibernateQueryBase(
    ISessionFactory sessionFactory)
    : base(sessionFactory) { }


    public abstract TResult Execute();
    }

    
    

  11. Add an empty INamedQuery interface, as shown in the following code:

    public interface INamedQuery
    {
    string QueryName { get; }
    }


    
    

  12. Add a NamedQueryBase class, as shown in the following code:

    public abstract class NamedQueryBase<TResult>
    : NHibernateQueryBase<TResult>, INamedQuery
    {


    protected NamedQueryBase(ISessionFactory sessionFactory)
    : base(sessionFactory) { }

    public override TResult Execute()
    {
    var nhQuery = GetNamedQuery();
    return Transact(() => Execute(nhQuery));
    }

    protected abstract TResult Execute(IQuery query);

    protected virtual IQuery GetNamedQuery()
    {
    var nhQuery = session.GetNamedQuery(
    ((INamedQuery) this).QueryName);
    SetParameters(nhQuery);
    return nhQuery;
    }

    protected abstract void SetParameters(IQuery nhQuery);

    public virtual string QueryName
    {
    get { return GetType().Name; }
    }
    }

    
    

  13. In Eg.Core.Data.Impl.Test, add a test fixture named QueryTests inherited from NHibernateFixture.
  14. Add the following test and three helper methods:

    [Test]


    public void NamedQueryCheck()
    {
    var errors = new StringBuilder();

    var queryObjectTypes = GetNamedQueryObjectTypes();
    var mappedQueries = GetNamedQueryNames();
    foreach (var queryType in queryObjectTypes)
    {
    var query = GetQuery(queryType);

    if (!mappedQueries.Contains(query.QueryName))
    {
    errors.AppendFormat(
    "Query object {0} references non-existent " +
    "named query {1}.",
    queryType, query.QueryName);
    errors.AppendLine();
    }
    }

    if (errors.Length != 0)
    Assert.Fail(errors.ToString());
    }

    private IEnumerable<Type> GetNamedQueryObjectTypes()
    {
    var namedQueryType = typeof(INamedQuery);
    var queryImplAssembly = typeof(BookWithISBN).Assembly;

    var types = from t in queryImplAssembly.GetTypes()
    where namedQueryType.IsAssignableFrom(t)
    && t.IsClass
    && !t.IsAbstract
    select t;
    return types;
    }

    private IEnumerable<string> GetNamedQueryNames()
    {
    var nhCfg = NHConfigurator.Configuration;

    var mappedQueries = nhCfg.NamedQueries.Keys
    .Union(nhCfg.NamedSQLQueries.Keys);
    return mappedQueries;
    }

    private INamedQuery GetQuery(Type queryType)
    {
    return (INamedQuery) Activator
    .CreateInstance(queryType,
    new object[] { SessionFactory });
    }

    
    

  15. For our example query, in the Queries namespace of Eg.Core.Data, add the following interface:

    public interface IBookWithISBN : IQuery<Book>
    {
    string ISBN { get; set; }
    }


    
    

  16. Add the implementation to the Queries namespace of Eg.Core.Data.Impl using the following code:

    public class BookWithISBN :
    NamedQueryBase<Book>, IBookWithISBN
    {
    public BookWithISBN(ISessionFactory sessionFactory)
    : base(sessionFactory) { }


    public string ISBN { get; set; }

    protected override void SetParameters(
    NHibernate.IQuery nhQuery)
    {
    nhQuery.SetParameter("isbn", ISBN);
    }

    protected override Book Execute(NHibernate.IQuery query)
    {
    return query.UniqueResult<Book>();
    }
    }

    
    

  17. Finally, add the embedded resource mapping, BookWithISBN.hbm.xml, to Eg.Core.Data.Impl with the following xml code:

    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping >
    <query name="BookWithISBN">
    <![CDATA[
    from Book b where b.ISBN = :isbn
    ]]>
    </query>
    </hibernate-mapping>