We recently started a project where I decided to use Castle’s ActiveRecord. On previous projects I had used NHibernate with a repository pattern, but for multiple reasons the active record pattern is more appropriate in this project.
One of the lowest hanging fruits of ActiveRecord is its abstraction from the NHibernate mapping files. Combine that with inheriting your domain objects from the ActiveRecordBase class and in no time you’re good to go with an object-oriented domain model that can easily be persisted to a relational database.
So far so good. But not too long into the project, you’ll find that need to start querying your domain model and then what do you do? Unfortunately, the query-side of Castle’s ActiveRecord is rather poorly documented. It’s kind of limited to this one page. For me that wasn’t enough to grasp the ideas behind it or give me a good feeling of what can be achieved with it. For example: there’s an ActiveRecordQuery class. What do I do with it? Do I inherit from it, like with ActiveRecordBase. And if so, what do I get? And what part is Castle specific and what part NHibernate specific?
Some sniffing on the internet did not reveal a whole lot more and that’s why I decided to open up the source code itself and find out for myself. Here I my findings for the most recent framework version at the time of this writing: 1.0.3 (RC3).
What does it add?
The query-side of Castle is a bit different from the basic active record side of the framework. On the latter side, things mainly evolve around the benefits you get from the ActiveRecordBase class by inheriting from it. There is no such thing for the query-side, which is actually a rather thin wrapper around the default NHibernate query functionality. It’s almost so thin, that you wonder why you should actually bother with the query-side of ActiveRecord. And there seems to be only one real compelling reason: the fact that it abstracts away from the NHibernate session. After all, the ActiveRecordBase hides the session as well and you wouldn’t really want to have to worry about an NHibernate session just to get your queries to work.
So what does the query-side of the framework offer? Well, actually a number of helper classes that aid in composing the right type of NHibernate query. It simply works as follows:
- instantiate a query helper object that suits the type of query you want to perform
- set all the necessary query properties on the query helper object
- get the query result by executing the query
It’s in the latter case that we see that there are two generations of query helper classes in the framework:
Generation 1 classes whose instances need to be passed into an ExecuteQuery() operation on an instance of an ActiveRecordBase child class; these are:
- HQLBasedQuery
- SimpleQuery
- ScalarQuery
- ActiveRecordCriteriaQuery
- CountQuery
Generation 2 classes that have their own public Execute() operation; these are:
- SimpleQuery<T>
- ScalarQuery<T>
- ActiveRecordMultiQuery
- ProjectionQuery<T,R>
- ProjectionQuery<T>
- ScalarProjectionQuery<T,R>
I’m not sure why the two generations exist, but I’m guessing this is due to architectural evolution.
The basics
First a quick tour of the basics.
IActiveRecordQuery
The mother of all query helper objects is the IActiveRecordQuery interface class. Primarily this establishes that each query can be executed as long as you pass in a NHibernate session object. This is done through the operation Execute(ISession).
Additionally it offers the RootType property, which returns the target type of the query.
IActiveRecordQuery<T>
Inherits from IActiveRecordQuery and is identical, with the exception that the Execute operation returns type T instead of an anonymous object.
ActiveRecordBaseQuery
This abstract class reroutes the IActiveRecordQuery’s Execute(ISession) operation to an InternalExecute(ISession) operation, which only ActiveRecord classes can get at. Furthermore it offers the hook to a query logger. Anyway, there’s not so much interesting things going on in this class. Things start to get interesting with the first real query helper class you can actually use: the HQLBasedQuery.
HQLBasedQuery
This is the helper class helps you to compose an NHibernate HQL query. Basically, an HQL query consists of a number of elements:
- The root type
- The actual query string
- The list of parameters passed into the query string
- A specification of the result range (start element and number of elements to be returned)
The HQLBasedQuery offers several constructors to set up your query with one simple statement. Part of these constructors in a QueryLanguage parameter, which allows you to specify it a query is an HQL (default) or SQL query.
The constructors allow to pass in parameters in a sequential fashion. However, NHibernate also supports named parameters. These name-value pairs need to be set separately with the SetParameter or SetParameterList operations.
So what does this class offer you? Well, next to the abstraction from the ISession, it offers you a different query building syntax. Here’s a quick comparison.
HqlBasedQuery q = new HqlBasedQuery(typeof(Cat), "from Cat where Name like ?", "Fritz%");
Object result = Cat.ExecuteQuery(q);
ISession session = ActiveRecordMediator.GetSessionFactoryHolder().CreateSession(typeof(Cat));Object result = session.CreateQuery("from Cat where Name like ?").SetParameter(0, "Fritz%").List();
As you can see, the hardest part in the NHibernate example is getting to the session object. The actual query execution is actually not all that different.
SimpleQuery and SimpleQuery<T>
The SimpleQuery class does exactly the same as the HQLBasedQuery, with the exception that the Execute(ISession) operation is now explicitly cast to a return type defined in the constructor or - if a return type is not specified - to root type.
With SimpleQuery<T> the exact same thing can be established as with SimpleQuery, only the return type is now T. Firthermore, since this is a generation 2 class it offers its own public Execute() operation. Great! So from .NET 2.0 and up, I’d use this over the non-generic type anytime.
ScalarQuery and ScalarQuery<T>
Just like SimpleQuery, this class does exactly the same as HQLBasedQuery, with the exception that it returns a single instance by applying the UniqueResult() operation on the query result. Strangely enough, there is no constructor that allows you to specify a return type, like we’ve seen for SimpleQuery. Then again these are .NET 2.0 and up days an so…
… we’d probably rather like to use ScalarQuery<T>, since it does return a strongly typed result and offers its own Execute() operation.
ActiveRecordCriteriaQuery
Strangely enough, this class inherits from HQLBasedQuery instead of ActiveRecordBaseQuery. It accepts a criteria array (each of type ICriterion) or a DetachedCriteria structure. No additional query modifiers (like parameters or result ranges) are supported in this class: if you need them, you should make them part of the passed in criteria.
There is one benefit this class gains from inheriting form HQLBasedQuery: if no criteria are passed in at all, an empty HQL query is executed upon execution, thus retuning all objects of the target type.
In essence this object gives you a slightly different syntax for defining and executing criteria-based queries. Here’s a syntax comparison.
ICriterion[] criteria = { Expression.Like("Name", "Fritz%") };ActiveRecordCriteriaQuery q = new ActiveRecordCriteriaQuery(typeof(Cat), criteria );
Object result = Cat.ExecuteQuery(q);
ISession session = ActiveRecordMediator.GetSessionFactoryHolder().CreateSession(typeof(Cat));Object result = session.CreateCriteria(typeof(Cat)).Add(Expression.Like("Name", "Fritz%")).List();
Hmmm… I’m not sure why I’d want to use the ActiveRecordCriteriaQuery instead of the NHibernate syntax. The latter seems less verbose.
And then I guess I would have expected an ActiveRecordCriteriaQuery<T> (along with its own Execute() operation), but unfortunately this does not exists. I’m not sure why.
CountQuery
This class creates a special HQLBasedQuery, namely one that executes a COUNT query. There are three constructors that allow us to specify the COUNT selection criteria:
- One with a HQL string based filter and accompanying optional parameters. The filter string needs to be the part that you’ll find in the WHERE clause of the HQL query “SELECT COUNT(*) FROM TargetType WHERE <filter>“.
- One with a set of NHibernate criteria of type ICriterion
- One with a set of NHibernate criteria of type DetachedCritera
- One without any selection criteria, which simply counts all the instances of the passed in target type.
When executed, the query returns an int32.
ActiveRecordMultiQuery
NHibernate offers MultiQuery functionality, which allows you to compile multiple queries and fire them off to the server in one call. The ActiveRecordMultiQuery is there to allow you to compose such a MultiQuery using ActiveRecodBaseQuery child classes. Note, however, that only non-criteria based queries are supported. If you use an ActiveRecordCriteriaQuery or a CountQuery, the ActiveRecordMultiQuery will throw an exception upon execution.
This class is actually a bit shaky in this version of the Castle framework. As you can see the Execite and Enumerate operations expose an NHibernate session unlike all the other classes. Furthermore, the Enumerate operation is not implemented and will throw an exception if you try to execute it. Personally I think this class it the weakest link in the bunch.
ProjectionQuery<ARType, TResultItem>
The projection query is the latest fashion in the framework and the result of great work by Ayende Rahien. He has taken some time to demonstrate the features of this query in a blog post or two.
The “latest fashion” exposes itself by the fact that:
- better use of generics: both he root type and the return type are specified as generics
- the use of the pipes and filters pattern for the SetRange operation, which allows you to apply a SetRange to the ProjectionQuery without altering the return type
- the public Execute operation
Actually, with this new fashion, one could even consider this class to be the first of an even newer third generation of query helper classes in the framework. What does strike me as odd is the exposure of NHibernate’s session in the Enumerate operation.
Upon execution of the query, an internal NHibernate criteria object is instantiated to which:
- the list of projections with which the class is initialized are applied using the SetProjection() operation
- the SetResultTransformer is applied to transform the result to the TRseultItem type
- the array of NHibernate Order expressions is applied, using the AddOrder for each entry. This allows you to indicate by which properties the result should be ordered.
ProjectionQuery<ARType>
Is exactly the same as ProjectionQuery, with the exception that the result is an object array instead of an IList<TResultItem>.
ScalarProjectionQuery<ARType, TResult>
Is identical to ProjectionQuery, with the exception that the result is a single TResultItem instead of an IList<TResultItem>, by applying the UniqueResult<TResult>() operation to the query result.
Conclusion
I think the exploration of the query-side of Castle’s ActiveRecord framework was well worth the effort to get a grasp of what its all about. I was a bit surprised to find that it is actually as thin as it is. Which leads me to question if it worth the hassle to learn the new query syntax imposed upon you by the classes’ API’s. This is especially bothersome, since:
- the API’s are poorly documented
- the API’s are inconsistent, not only across the generations, but also within the generations
The first issue I hope to have solved somewhat with this blog post, by giving some insight of how these classes relate to the NHibernate constructs, that are far better documented. The latter is hard to solve. The most obvious reason for this being backward compatibility.
These are all compelling reasons for me to consider sticking with basic NHibernate queries. I’m thinking it is worth the extra overhead of retrieving the right NHibernate session in order to execute a query. This could easily be established by creating an ActiveRecordHelper-like class with a GetSession(RootType) operation. Once this is implemented, the query is actually rather straightforward and uses regualr NHibernate syntax, e.g.:
Object result = ActiveRecordHelper.GetSession(Cat).CreateCriteria(typeof(Cat)).Add(Expression.Like("Name", "Fritz%")).List();
Anyway, I'm sure I've overlooked a thing or two, so your thoughts and comments on this are more than welcome!