Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

NHibernate : Handling the Special Cases

In my last NHibernate post I mentioned that NHibernate is great because it does the heavy lifting for the common tasks that make up 95% of my data access layer, while letting me tweak the remaining 5% as needed.

Let’s look at some of the ways you can tweak, or even circumvent NHibernate for those queries that require some extra attention. Do note that there are a lot of different solutions – we’re only looking at a few.

The most obvious method is to use named queries and custom SQL statements in your mapping. This provides you a lot of flexibility without diverging from NHibernate’s realm. The NHibernate reference has good documentation on this approach, so we won’t cover it (check out section 14.2 “Named Queries”, 14.3 “Custom SQL for create, update and delete” and 14.4 “Custom SQL for loading”).

The next option is to use an ISqlQuery via the ISession.CreateSqlQuery. Unlike an IQuery which works against your domain using HQL, an ISqlQuery is closely tied to your database and SQL. However, you still get access to NHibernate parameter substitutions, paging capabilities and O/R mapping (if you want). Again, the NHibernate reference has a very detailed section, check out 14.1 “Using an ISQLQuery”.

You can optimize your Deletes by supplying the ISession.Delete command some HQL rather than the instance you want to delete. For example, if you want to delete all comments belonging to a blog post, you can do:

session.Delete("from Comment where PostId = :id", postId, NHibernateUtil.Int32);

More recently the NHibernate team made it possible to execute any statement (insert/delete/update) from an IQuery. You can find out more from this post by Ayende.

When necessary (typically bulk imports or other bulk operations), you can circumvent NHibernate’s first-level cache by using an IStatelessSession rather than an ISession. Rather than calling OpenSession on your ISessionFactory, simply call OpenStatlessSession. Keep in mind that the IStatelessSession doesn’t have the exact same API as an ISession.

When things get really crazy, you can always get an ADO.NET connection from the ISession’s Connection property. With a raw database connection, the world is your oyster. You can always write your code in such a way that you have direct access to our connection string to create your own connection, thus completely and fully circumventing NHibernate.

Finally, any of these methods can be combined with some simple designs in order to ensure that everything is neat and tidy:

public abstract class Repository : IRepository
{
    public void Delete<T>(T entity)
    {
        WithinTransaction(s => s.Delete(target));
    }

    protected void WithinTransaction(Action action)
    {
        WithinTransaction(Session, action);
    }        
    protected static void WithinTransaction(ISession session, Action action)
    {
        var transaction = session.BeginTransaction();
        try
        {
            action();
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
            throw;
        }
        finally
        {
            transaction.Dispose();
        }
    }
}


public class PostRepository : Repositor, IPostRepository
{
    public void Delete(int postId)
    {
        WithinTransaction(s => 
            {
                session.Delete("from Comment where PostId = :id", postId, NHibernateUtil.Int32);
                session.Delete("from Post where Id = :id", postId, NHibernateUtil.Int32);
            });
    }
}

Once you understand NHibernate’s flexibility and understand your code’s hot spots, it generally takes a pretty trivial amount of tweaking to speed things up. While the changes you make, in the name of performance, to your models (both domain and data) may be disgusting rest assured that HHibernate will not stand in your way. Be warned though that many of the above solutions may have side effects with respect to NHibernate’s 1st and 2nd level caches. However, if you were to custom write everything, including caching, you’d likely have similar synch issues.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

5 Responses to NHibernate : Handling the Special Cases

  1. Martin says:

    There’s a typo in the title of the post: NHibnerate -> NHibernate

  2. karl says:

    @Bill:
    Because they rollback the transaction in the case of an exception, something you can’t do with a using.

  3. Why did they use a try..finally with the transaction rather than a using statement?

  4. karl says:

    @Paco:
    That repository snippet comes from FluentNhibernate. Seems to be flavor of the day in order to achieve really simple/small implementations for basic CRUD sites without having to write a lot of infrastructure. The approach definitely works for a number of use case, and not for others.

  5. Paco says:

    Why do you manage the transaction in the repository? What if you need 2 queries on the same transaction?