Data Access With NHibernate

19 commentsWritten on June 23rd, 2008 by
Categories: NHibernate

One thing that keeps amazing me is how many smart developers still feel the urge to write their own data access layer (DAL). They either do it all manually, or they generate parts of it, or they generate the whole thing. Whichever way you go, there are still various alternative paths you can choose from. Some people like to use stored procedures for everything, some people generate sql statements and provide a semi-OO API in front of it, some people still spend time constructing Command objects. I think i've seen most approaches by now, and my personal opinion is that they pretty much all suck.

These DAL's usually have at least one big downside to them. Generated DAL's are usually pretty good for productivity but most of the times they don't really offer you that much control over how queries should be executed, whether or not statements should be batched, specify fetching-strategies, etc. Hand-written DAL's are terrible for productivity but you can fine tune each action to an optimal implementation. These downsides are pretty big IMHO, and are often underestimated. Sometimes due to semi-religious stances and sometimes it's just due to ignorance.

As you already know, i'm a fan of NHibernate. Well, consider me a fan of decent ORM's in general. NHibernate just happens to be the one i know best and am very happy with. In this post i'd like to take a look at how you can easily offer the most common data access requirements without generating code, while still allowing high developer productivity. We'll go over the implementation of a generic Repository class, and hopefully you'll see how much NHibernate can improve your way of working.

We're going to look at the implementation piece by piece, so lets just start with the declaration of the class so we can get that out of the way:

    public class Repository<T> : IRepository<T>

As you can see, this is just a generic class that takes a type parameter. The type parameter represents the type of the Entity you want this repository to handle. If you have an Entity base class or interface, you probably want to restrict the type of T to inherit from Entity or implement IEntity or whatever.

This class needs to be able to access the current NHibernate session, which i discussed yesterday:

        private readonly IActiveSessionManager activeSessionManager;

 

        public Repository(IActiveSessionManager activeSessionManager)

        {

            this.activeSessionManager = activeSessionManager;

        }

 

        protected ISession Session

        {

            get { return activeSessionManager.GetActiveSession(); }

        }

Right, now we can actually get to the interesting Data Access parts. Obviously, each DAL needs a way to retrieve a specific entity based on its Primary Key value:

        /// <summary>

        /// Retrieves the entity with the given id

        /// </summary>

        /// <param name="id"></param>

        /// <returns>the entity or null if it doesn't exist</returns>

        public T Get(object id)

        {

            return Session.Get<T>(id);

        }

Very straightforward stuff... this simply uses the current NHibernate session to retrieve an entity of the requested type, with the given primary key value.

Another thing we need is a way to create or update entity instances:

        /// <summary>

        /// Saves or updates the given entity

        /// </summary>

        /// <param name="entity"></param>

        public void SaveOrUpdate(T entity)

        {

            Session.SaveOrUpdate(entity);

        }

This method simply uses the session's SaveOrUpdate method, which will either perform an Insert (in case of a new entity) or an Update (in case of an existing entity). NHibernate will perform a check for a new instance depending on how you've configured this in your mapping files.

So far this has been really straightforward, so it's time to get to a more interesting part. Retrieving entities based on criteria (basically a query):

        /// <summary>

        /// Returns each entity that matches the given criteria

        /// </summary>

        /// <param name="criteria"></param>

        /// <returns></returns>

        public IEnumerable<T> FindAll(DetachedCriteria criteria)

        {

            return criteria.GetExecutableCriteria(Session).List<T>();

        }

This probably needs a bit of explaining. The parameter is an instance of the DetachedCriteria type. A DetachedCriteria is just like a Criteria instance, except that it hasn't been associated with a session yet. So you can create the DetachedCriteria without being connected to a session. This basically represents a query. I'll show a concrete example of this later on. The thing to remember is that this is the only code you really need to perform any query you want. You just have to write the query using the Criteria API. This does have a bit of a learning curve, but most people pick it up pretty fast.

You usually also want a way to determine the ordering of the result of the query:

        /// <summary>

        /// Returns each entity that maches the given criteria, and orders the results

        /// according to the given Orders

        /// </summary>

        /// <param name="criteria"></param>

        /// <param name="orders"></param>

        /// <returns></returns>

        public IEnumerable<T> FindAll(DetachedCriteria criteria, params Order[] orders)

        {

            if (orders != null)

            {

                foreach (var order in orders)

                {

                    criteria.AddOrder(order);

                }

            }

 

            return FindAll(criteria);

        }

Each order you provide is applied to the query and then the query is executed. Again, pretty simple stuff right?

You want to know how you can get paging working? Here it is:

        /// <summary>

        /// Returns each entity that matches the given criteria, with support for paging,

        /// and orders the results according to the given Orders

        /// </summary>

        /// <param name="criteria"></param>

        /// <param name="firstResult"></param>

        /// <param name="numberOfResults"></param>

        /// <param name="orders"></param>

        /// <returns></returns>

        public IEnumerable<T> FindAll(DetachedCriteria criteria, int firstResult, int numberOfResults, params Order[] orders)

        {

            criteria.SetFirstResult(firstResult).SetMaxResults(numberOfResults);

            return FindAll(criteria, orders);

        }

Keep in mind that the executed query only retrieves the results within the paging range. It does not retrieve everything to perform the paging client-side, this happens in the db where it's supposed to happen.

What if you have a query that should only return one instance? That's easy to do as well:

        /// <summary>

        /// Returns the one entity that matches the given criteria. Throws an exception if

        /// more than one entity matches the criteria

        /// </summary>

        /// <param name="criteria"></param>

        /// <returns></returns>

        public T FindOne(DetachedCriteria criteria)

        {

            return criteria.GetExecutableCriteria(Session).UniqueResult<T>();

        }

What if you have a query and you just want the very first result instead of the entire resultset? Again, pretty easy to do:

        /// <summary>

        /// Returns the first entity to match the given criteria

        /// </summary>

        /// <param name="criteria"></param>

        /// <returns></returns>

        public T FindFirst(DetachedCriteria criteria)

        {

            var results = criteria.SetFirstResult(0).SetMaxResults(1)

                .GetExecutableCriteria(Session).List<T>();

 

            if (results.Count > 0)

            {

                return results[0];

            }

 

            return default(T);

        }

Again, NHibernate will issue a smart sql statement... that is, it only retrieves the first result instead of the entire resultset.

Obviously, this is also useful if you can define the order of the results to pick the first result:

        /// <summary>

        /// Returns the first entity to match the given criteria, ordered by the given order

        /// </summary>

        /// <param name="criteria"></param>

        /// <param name="order"></param>

        /// <returns></returns>

        public T FindFirst(DetachedCriteria criteria, Order order)

        {

            return FindFirst(criteria.AddOrder(order));

        }

How often have you seen developers execute a query in code, only to use the count of the records without actually needing the returned records? We no longer need to beat the shit out of these developers:

        /// <summary>

        /// Returns the total number of entities that match the given criteria

        /// </summary>

        /// <param name="criteria"></param>

        /// <returns></returns>

        public long Count(DetachedCriteria criteria)

        {

            return Convert.ToInt64(criteria.GetExecutableCriteria(Session)

                .SetProjection(Projections.RowCountInt64()).UniqueResult());

        }

In this case, NHibernates issues a simple select count... query based on the criteria you've provided. Nice huh?

We might as well throw in this one as well:

        /// <summary>

        /// Returns true if at least one entity exists that matches the given criteria

        /// </summary>

        /// <param name="criteria"></param>

        /// <returns></returns>

        public bool Exists(DetachedCriteria criteria)

        {

            return Count(criteria) > 0;

        }

And last, but certainly not least, you'll also want a way to delete entities from the database. How about this:

        /// <summary>

        /// Deletes the given entity

        /// </summary>

        /// <param name="entity"></param>

        public void Delete(T entity)

        {

            Session.Delete(entity);

        }

 

        /// <summary>

        /// Deletes every entity that matches the given criteria

        /// </summary>

        /// <param name="criteria"></param>

        public void Delete(DetachedCriteria criteria)

        {

            // a simple DELETE FROM ... WHERE ... would be much better, but i haven't found

            // a way to do this yet with Criteria. So now it does two roundtrips... one for

            // the query, and one with all the batched delete statements (that is, if you've

            // enabled CUD statement batching

            foreach (T entity in FindAll(criteria))

            {

                Delete(entity);

            }

        }

The first Delete method simply deletes the given entity. The second method probably needs to be explained a bit more. This method receives a query, and it deletes all the items that the query returns. As you can see from the comment, it would be better if it would perform a DELETE FROM ... WHERE instead of fetching the results of the query but i didn't find a way to do that with the criteria API. In a more advanced scenario, this might actually be better than simply issuing a large DELETE statement because you could offer a Delete method which also receives a block of code to execute before or after each delete is executed. Which opens the door to a lot of interesting opportunities.

And that's it basically... I really haven't shown you that much code right? And what does this code offer us? We get create/update/delete functionality, and we also have some nice options for querying our data. And since the Criteria API of NHibernate allows you to create powerful and complex queries, you can use it to create your queries and then you simply pass these criteria to the repository to fetch the data. Just so we're clear on this, you're not just limited to specifying which data you want to retrieve, but you can also tell NHibernate how you want to retrieve it because you can define fetching strategies for each association. This is a tremendously powerful feature which offers you a lot of flexibility in choosing the most optimal data fetching approaches, without being limited to what your DAL supports or having to spend a lot of code on it.

Let's wrap up this post with a small example of how you could use this repository class to execute a query you wrote yourself:

            var criteria = DetachedCriteria.For<ProductCategory>()

                .Add(Expression.Like("Name", "Test%"));

 

            var categories = repository.FindAll(criteria);

As you can see, this is really easy. The only effort basically lies within building the query, so as you're queries become more complex, this effort obviously increases.

If you combine this repository approach with query batching, you end up with an easy-to-use data layer which offers you all the flexibility you could want, while still allowing you to implement specifically tuned solutions to achieve excellent performance. Also, keep in mind that this is only a very basic repository implementation. The Rhino Commons repository implementation offers even more functionality with a couple of extra options to boost performance. I really can't think of a single good reason not to use this approach anymore.

  • http://davesquared.blogspot.com David

    Thanks for this! This is a great summary of the fundamental queries you need to work with NHibernate. I only have very basic experience with NHibernate, and this has really helped clarify things for me.
    Cheers,
    David

  • Mike Roosa

    Davy,
    I’m new to .net and data access layers. I work for a company that has an IBM Universe Database. This is an multivalue database that is quite a bit different than SQL Server. The provide a dll for accessing the data but SQL queries, stored procedures don’t work. They have their own query language. Can I use nhibernate with this or would I need to build this data access layer by hand?

  • http://davybrion.com Davy Brion

    Hi Mike,

    http://www.hibernate.org/361.html#A7

    According to that, DB2 UDB is indeed supported. However, i would recommend that you definitely experiment with it to make sure it indeed does everything you need it to before you actually commit to using it in a real world project. Better safe than sorry :)

  • http://yoot.be Stiiifff

    Hello Davy,

    NHibernate is leaking in your repository implementation … a repository is supposed to give a layer of abstraction between your business logic and your data access logic. This is not the case here. What you call repository in this case is nothing more than a Dao.

    I’ve written some post about a generic Linq-friendly repository (inspired by several open source projects and blog posts):
    http://blog.yoot.be/post/Domain-Driven-Design-with-Linq–ORM2c-Part-2.aspx

    Who wants to use HQL or NHibernate criteria when Linq is around ? ;o)

  • http://davybrion.com Davy Brion

    Considering the fact that Linq for NHibernate is not complete yet, i’d much rather stick to something that actually works :)

  • http://yoot.be Stiiifff

    Yep, and that’s too bad, Most of other non-MS ORM already have their provider for Linq. NHibernate is probably losing potential users to Linq2Sql / Entity Framework just because of that. This should the #1 priority of NHibernate contributors. ;o)

  • http://davybrion.com Davy Brion

    i think they’re targeting LINQ support for NH 2.1… not sure though, there are reasons why they can’t easily provide a full implementation now. Check the developers mailinglist for more details since it was brought up very recently

    Regarding your earlier comment that repositories should provide a layer of abstraction between the business logic and the data access logic: in theory, you are correct. Practically speaking though, what’s the point in providing another layer of abstraction for NHibernate’s ICriteria? It’s not like you’re just gonna switch ORM’s in the middle of a project. And since you’d have to write more code to provide the abstraction you should think about the added value of that code. Is there really a benefit? Wouldn’t it be easier to just extend this repository class with specific versions for each entity that needs a repository?

    for instance, you could easily provide a ProductsRepository class which extends this repository class. In the derived repository you could provide clean methods like FindProductByName or whatever that only take the required parameters, and the implementation of that method would only need to construct the criteria and pass it on the base repository implementation. Expose only the clean methods through an IProductsRepository interface, have your business code communicate only with the repository through that interface and voila, you’re done without needing to do unnecessary work to achieve purely theoretical ‘correctness’ :p

  • http://yoot.be Stiiifff

    Davy, I agree that there is always a gap between theory and practice. I usually am a very down-to-earth guy ;o) lol

    What we are talking about here is also a business decision, not just a technical one: if you write repositories strongly-coupled with NHibernate, it means the developers that will work on your app must know how to use NHibernate’s HQL / Criteria API … the time you gain by ‘writing less code’, you might loose it in training, debug, support. Also, as you say, you’re not gonna change of ORM in the middle of a project, or between phases / releases … so, you might want to choose an ORM that provides you with the safest future evolution path. And I think that today, starting a project with an ORM that doesn’t support Linq is a little bit ‘annoying’, to say the least.

    It also depends on the size of your project, you might not want to add an extra layer of abstraction for small projects … but you would probably consider it on ‘hundreds of entities & thousands of business rules’ projects.

    Concerning the ProductsRepository, sorry but I still call it a Dao. ;o) In essence, what you’re doing is moving your stored procedures out of the database. Why do I need to give a name (=> method of your strongly-typed repository) to every possible query I need in the application ?

    In you don’t mind your ORM to leak in your repository interface, a nice alternative is to have a generic repository that accept ORM-specific query criterion.
    Your business components can then query directly on your generic repository (Ayende has a nice post called ‘Querying is a business concern’). If you want to re-use some of your queries, you might want to check the Specification pattern. The public API of your Business layer will be fine as long as it doesn’t expose any ORM-specific type. This also implies that you will have to component-ize your business layer, which is a source of many other design questions. lol

    The generic repository of Rhino.Commons is actually designed that way … you can derive from it, but you should only do so if you really need it (very complex queries that you want to encapsulate) … there is no point of creating a lot of methods for simple queries. For most cases, you can just use the generic repository and simply pass it your query criterion.

  • http://yoot.be Stiiifff

    One more interesting info : you might want to check out the MSDN article of Ayende where he is describing ‘Generic Specialization’ applied to Rhino.Commons’s IRepository using Windsor container:
    http://msdn2.microsoft.com/en-us/library/aa973811.aspx

    This confirms the idea of ‘write a strongly-typed repository only if you really need it’.

  • http://davybrion.com Davy Brion

    i don’t really see the difference between Rhino.Common’s generic repository leaking NHibernate specific details (such as Criteria) being ‘ok’ and mine, especially since mine is just a simplified version of Rhino.Common’s generic repository ;)

    As for the business decision, i would much rather go with an ORM because of the qualities that it has instead of simply going with an ORM because another candidate doesn’t support LINQ yet. LINQ is nice, but it is not that important. Even though LINQ is supposed to be a unified syntax and should in theory require less training, i don’t really see that happening. Each ORM and each LINQ provider will have its own little quirks. The syntax might be the same, but the behavior might be somewhat different from provider to provider. The results should be the same, but a fast performing statement with one provider might be horrible with another. Merely going for LINQ because of the unified querying syntax is a bit shortsighted ;)

  • http://yoot.be Stiiifff

    Of course, they are the same (and both ‘ok’ ;) lol) … my second post was more about the choice of creating a strongly-typed repository for every entity type with finder methods (hence the ‘Dao’ term), or use the generic one most of the time and derive when really necessary.

    Don’t get me wrong, I wouldn’t choose an ORM over another just because of Linq support … it’s a matter of common sense. ;)

  • Pingback: Learn How to use NHibernate with the Summer of NHibernate Screencast Series - Readed By Wrocław NUG members - Wroc.NET

  • Pingback: Data Access With NHibernate - NHibernate blog - NH Forge

  • http://blog.unhandled-exceptions.com Steve Bohlen

    Just as a suggestion to avoid the ‘exposure’ of the NHib Criteria API outside your data-access-layer, I always recommend the following pattern…

    1) create a Repository class much as you have outlined

    2) we make ours abstract so that consumers MUST derive from it in order to use it as in…

    3) public class OrderRepository : Repository {…}

    4) within the OrderRepository class, we create methods like OrderRepository.GetOrdersSince(datetime orderDate) {…} that *internally* make use of the base Repository methods you suggest (Find, FindOne, etc. and can properly leverate the ICriteria API

    5) callers of the data access methods then follow a pattern like…

    OrderRespository orderRepository = new OrderRepository();
    orderRepository.GetOrdersSince(’1/1/2007′);

    …and consuming code NEVER explicity does…

    Repository orderRepository = new Repository();

    …and in fact cannot since Repository is intentionally abstract. This way we get the goodness of the generics-enabled repository approch where the guts of the messy NHib-related code resides but the fact that NHib is in there somewhere isn’t exposed to callers of the data-access-methods.

  • http://blog.unhandled-exceptions.com Steve Bohlen

    Sorry, in that post the #3 item’s class declaration was supposed to read…

    public class OrderRepository : Repository {…}

    (I forgot the ‘of T’ part of the derivation statement~!)

  • http://blog.unhandled-exceptions.com Steve Bohlen

    Whoops — wordpress is actually removing my angle-brackets from my comments. Imaging that was..

    public class OrderRepository : Repository[Order] {…}

    where the [ ] are actually supposed to angle-brackets :)

    Sorry for the multi-posts, took me a bit to realize what was going on (which is odd since my own blog is also using wordpress and I have the same issue(s)~!)

  • http://davybrion.com Davy Brion

    @Steve

    that’s a good approach, i usually try to do the same, although my base repository isn’t abstract.

    In situations where you want to take advantage of NHibernate’s MultiCriteria, it’s hard not to leak the DetachedCriteria though. In those cases, i really can’t be bothered with providing an abstraction that doesn’t really provide a true benefit.

  • Pingback: The Inquisitive Coder - Davy Brion’s Blog » Blog Archive » Transparent Query Batching Through Your Repository

  • Ranu Mandan

    Hi,
    Do you have generic NHibernate DAL for download ?