NHibernate Forge
The official new home for the NHibernate for .NET community

QueryOver in NH 3.0


The ICriteria API is NHibernate's implementation of Query Object. NHibernate 3.0 introduces the QueryOver api, which combines the use of Extension Methods and Lambda Expressions (both new in .Net 3.5) to provide a statically typesafe wrapper round the ICriteria API.

QueryOver uses Lambda Expressions to provide some extra syntax to remove the 'magic strings' from your ICriteria queries.

So, for example:

.Add(Expression.Eq("Name", "Smith"))


.Where<Person>(p => p.Name == "Smith")

With this kind of syntax there are no 'magic strings', and refactoring tools like 'Find All References', and 'Refactor->Rename' work perfectly.

Note: QueryOver is intended to remove the references to 'magic strings' from the ICriteria API while maintaining it's opaqueness. It is not a LINQ provider; NHibernate 3.0 has a built-in Linq provider for this.


Structure of a Query

Queries are created from an ISession using the syntax:

IList<Cat> cats =
        .Where(c => c.Name == "Max")


Detached QueryOver (analagous to DetachedCriteria) can be created, and then used with an ISession using:

QueryOver<Cat> query =
        .Where(c => c.Name == "Paddy");
IList<Cat> cats =


Queries can be built up to use restrictions, projections, and ordering using a fluent inline syntax:

var catNames =
        .WhereRestrictionOn(c => c.Age).IsBetween(2).And(8)
        .Select(c => c.Name)
        .OrderBy(c => c.Name).Asc


Simple Expressions

The Restrictions class (used by ICriteria) has been extended to include overloads that allow Lambda Expression syntax. The Where() method works for simple expressions (<, <=, ==, !=, >, >=) so instead of:

ICriterion equalCriterion = Restrictions.Eq("Name", "Max")

You can write:

ICriterion equalCriterion = Restrictions.Where<Cat>(c => c.Name == "Max")


Since the QueryOver class (and IQueryOver interface) is generic and knows the type of the query, there is an inline syntax for restrictions that does not require the additional qualification of class name. So you can also write:

var cats =
        .Where(c => c.Name == "Max")
        .And(c => c.Age > 4)

Note, the methods Where() and And() are semantically identical; the And() method is purely to allow QueryOver to look similar to HQL/SQL.


Boolean comparisons can be made directly instead of comparing to true/false:

        .Where(p => p.IsParent)
        .And(p => !p.IsRetired)


Simple expressions can also be combined using the || and && operators. So ICriteria like:

            Restrictions.Eq("Name", "test name"),
                Restrictions.Gt("Age", 21),
                Restrictions.Eq("HasCar", true))))

Can be written in QueryOver as:

        .Where(p => p.Name == "test name" && (p.Age > 21 || p.HasCar))


Each of the corresponding overloads in the QueryOver API allows the use of regular ICriterion to allow access to private properties.

        .Where(Restrictions.Eq("Name", "Max"))


It is worth noting that the QueryOver API is built on top of the ICriteria API. Internally the structures are the same, so at runtime the statement below, and the statement above, are stored as exactly the same ICriterion. The actual Lambda Expression is not stored in the query.

        .Where(c => c.Name == "Max")


Additional Restrictions

Some SQL operators/functions do not have a direct equivalent in C#. (e.g., the SQL where name like '%anna%'). These operators have overloads for QueryOver in the Restrictions class, so you can write:

        .Where(Restrictions.On<Cat>(c => c.Name).IsLike("%anna%"))

There is also an inline syntax to avoid the qualification of the type:

        .WhereRestrictionOn(c => c.Name).IsLike("%anna%")


While simple expressions (see above) can be combined using the || and && operators, this is not possible with the other restrictions. So this ICriteria:

            Restrictions.Gt("Age", 5)
            Restrictions.In("Name", new string[] { "Max", "Paddy" })))

Would have to be written as:

            Restrictions.Where<Cat>(c => c.Age > 5)
            Restrictions.On<Cat>(c => c.Name).IsIn(new string[] { "Max", "Paddy" })))



QueryOver can navigate association paths using JoinQueryOver() (analagous to ICriteria.CreateCriteria() to create sub-criteria).

The factory method QuerOver<T>() on ISession returns an IQueryOver<T>. More accurately, it returns an IQueryOver<T,T> (which inherits from IQueryOver<T>).

An IQueryOver has two types of interest; the root type (the type of entity that the query returns), and the type of the 'current' entity being queried. For example, the following query uses a join to create a sub-QueryOver (analagous to creating sub-criteria in the ICriteria API):

IQueryOver<Cat,Kitten> catQuery =
        .JoinQueryOver(c => c.Kittens)
            .Where(k => k.Name == "Tiddles");

The JoinQueryOver returns a new instance of the IQueryOver than has its root at the Kittens collection. The default type for restrictions is now Kitten (restricting on the name 'Tiddles' in the above example), while calling .List() will return an IList<Cat>. The type IQueryOver<Cat,Kitten> inherits from IQueryOver<Cat>.

Note, the overload for JoinQueryOver takes an IEnumerable<T>, and the C# compiler infers the type from that. If your collection type is not IEnumerable<T>, then you need to qualify the type of the sub-criteria:

IQueryOver<Cat,Kitten> catQuery =
        .JoinQueryOver<Kitten>(c => c.Kittens)
            .Where(k => k.Name == "Tiddles");


The default join is an inner-join. Each of the additional join types can be specified using the methods .Inner, .Left, .Right, or .Full. For example, to left outer-join on Kittens use:

IQueryOver<Cat,Kitten> catQuery =
        .Left.JoinQueryOver(c => c.Kittens)
            .Where(k => k.Name == "Tiddles");



In the traditional ICriteria interface aliases are assigned using 'magic strings', however their value does not correspond to a name in the object domain. For example, when an alias is assigned using .CreateAlias("Kitten", "kittenAlias"), the string "kittenAlias" does not correspond to a property or class in the domain.

In QueryOver, aliases are assigned using an empty variable. The variable can be declared anywhere (but should be empty/default at runtime). The compiler can then check the syntax against the variable is used correctly, but at runtime the variable is not evaluated (it's just used as a placeholder for the alias).

Each Lambda Expression function in QueryOver has a corresponding overload to allow use of aliases, and a .JoinAlias function to traverse associations using aliases without creating a sub-QueryOver.

Cat catAlias = null;
Kitten kittenAlias = null;

IQueryOver<Cat,Cat> catQuery =
    session.QueryOver<Cat>(() => catAlias)
        .JoinAlias(() => catAlias.Kittens, () => kittenAlias)
        .Where(() => catAlias.Age > 5)
        .And(() => kittenAlias.Name == "Tiddles");



Simple projections of the properties of the root type can be added using the .Select method which can take multiple Lambda Expression arguments:

IList selection =
            c => c.Name,
            c => c.Age)

Because this query no longer returns a Cat, the return type must be explicitly specified. If a single property is projected, the return type can be specified using:

IList<int> ages =
        .Select(c => c.Age)

However, if multiple properties are projected, then the returned list will contain object arrays, as per a projection in ICriteria. This could be fed into an anonymous type using:

var catDetails =
            c => c.Name,
            c => c.Age)
        .Select(properties => new {
            CatName = (string)properties[0],
            CatAge = (int)properties[1],

Note that the second .Select call in this example is an extension method on IEnumerable<T> supplied in System.Linq; it is not part of NHibernate.


QueryOver allows arbitrary IProjection to be added (allowing private properties to be projected). The Projections factory class also has overloads to allow Lambda Expressions to be used:

IList selection =
            .Add(Projections.Property<Cat>(c => c.Name))
            .Add(Projections.Avg<Cat>(c => c.Age)))


In addition there is an inline syntax for creating projection lists that does not require the explicit class qualification:

IList selection =
        .SelectList(list => list
            .Select(c => c.Name)
            .SelectAvg(c => c.Age))


Projections can also have arbitrary aliases assigned to them to allow result transformation. If there is a CatSummary DTO class defined as:

public class CatSummary
    public string Name { get; set; }
    public int AverageAge { get; set; }

... then aliased projections can be used with the AliasToBean<T> transformer:

CatSummary summaryDto = null;
IList<CatSummary> catReport =
        .SelectList(list => list
            .SelectGroup(c => c.Name).WithAlias(() => summaryDto.Name)
            .SelectAvg(c => c.Age).WithAlias(() => summaryDto.AverageAge))


The Subqueries factory class has overloads to allow Lambda Expressions to express sub-query restrictions. For example:

QueryOver<Cat> maximumAge =
        .SelectList(p => p.SelectMax(c => c.Age));

IList<Cat> oldestCats =
        .Where(Subqueries.WhereProperty<Cat>(c => c.Age).Eq(maximumAge))


The inline syntax allows you to use subqueries without requalifying the type:

IList<Cat> oldestCats =
        .WithSubquery.WhereProperty(c => c.Age).Eq(maximumAge)


There is an extension method As() on (a detached) QueryOver that allows you to cast it to any type. This is used in conjunction with the overloads Where(), WhereAll(), and WhereSome() to allow use of the built-in C# operators for comparison, so the above query can be written as:

IList<Cat> oldestCats =
        .WithSubquery.Where(c => c.Age == maximumAge.As<int>())


Posted dic 17 2009, 11:22 p.m. by Richard Brown


lefleau wrote re: QueryOver in NH 3.0
on 12-17-2009 22:25

Nice post.

Where or when can we download NH 3.0 ?

uberVU - social comments wrote Social comments and analytics for this post
on 12-17-2009 23:22

This post was mentioned on Twitter by nhibernate: Blogged: QueryOver in NH 3.0: Introduction The ICriteria API is NHibernate's implementation of Query Objec... http://bit.ly/6b7y2d

Richard Brown wrote re: QueryOver in NH 3.0
on 12-18-2009 6:22


This is currently only in the trunk.  I think there might be an alpha release soon, but in the meantime your options are:

1.  Download the source and build it;

2.  Go to TeamCity, where the CI build outputs the binaries;

3.  Go to HornGet to get their latest binaries.



Omer Mor wrote re: QueryOver in NH 3.0
on 12-18-2009 6:28

Great stuff!

Regarding the alias API:

would it be possible to use:

  session.QueryOver(ref catAlias)

instead of

  session.QueryOver<Cat>(() => catAlias)


Richard Brown wrote re: QueryOver in NH 3.0
on 12-18-2009 12:08

Hi Omer,

I don't think so.  The API relies on using Lambda Expression syntax which compiles a LINQ expression that can be examined at runtime.

A regular variable (in this example passed as a reference) would not allow runtime examination of the variable name.



Sven Devriese wrote re: QueryOver in NH 3.0
on 12-21-2009 7:51

Richard, exciting stuff! These will be welcome changes but please don't forget about us poor VB.NET developers.

I know most "real" programmers don't even consider it but is there and quite popular too.

What is my gripe ? Well, if you translate the first query to VB.NET it goes like this:

Dim cats As IList(Of Cat) = _

   session.QueryOver(Of Cat)()

(leaving out the Where stuff for now)

This is doesn't quite "pronounce" easily. In fact I thought

the generic <> symbols were pronounced "of" by the c#

guys as well.

It becomes even worse with the second query (assuming that isn't a typo):

Dim query as QueryOver(Of Cat) = _

   QueryOver.Of(Of Cat)()

(again leaving out the where stuff)

Thanks for your (and the other NH developers') consideration


Richard Brown wrote re: QueryOver in NH 3.0
on 12-21-2009 9:23

Hi Sven,

I agree that this is slightly bizarre use of English, and I must admit I hadn't considered VB.Net syntax (I've never used it personally).

C# programmers do tend to use the term "Of" conversationally too ... but we don't usually let it get in the way of allowing something like:  DetachedCriteria.For<Person>(), or session.Get<Person>(3)

I think the use of generics these days (especially in fluent frameworks) goes way beyond the original intentions of the feature, so I suspect a certain leeway is often allowed.

It's hard to please everyone, but I'm open to suggestions.  Do you have an alternative proposal for the syntax?



Sven Devriese wrote re: QueryOver in NH 3.0
on 12-21-2009 9:57

Hi Richard, thanks for your consideration.

How very smart of you to reply with a question :-)

I'll be the first to admit that the "Of" syntax in VB.NET

(almost) only makes sense in the classic generic collection scenario i.e. Generic.IList(Of Customer)

IMHO the VB.NET compiler team dropped the ball on that choice. As you wrote, generics these days is about much more than strongly typed collections.

So we could wait for the VB.NET compiler team to come to their senses and follow the C# team's lead, as they have done with Nullables. Originally the syntax was thus:

Dim a as Nullable(Of DateTime)

whereas as of VB9 we can write this:

Dim a as DateTime?

So maybe in VB.NExT the following syntax would be valid:

Dim query As QueryOver<Cat> = _


However, I'm not holding my breath so my personal suggestion for NH's new QueryOver API is to drop the

"Over" in QueryOver because it's that preposition that's

conflicting with "Of".

Of course, I'm not holding my breath either in anticipation of you guys actually making that change but then again it's Christmas. Why not believe in Santa ? ;-)



Richard Brown wrote re: QueryOver in NH 3.0
on 12-21-2009 10:37

Hi Sven,

Thanks for the suggestion.  Unfortunately (or unsurprisingly) we already have a CreateQuery() and an IQuery for HQL in NHibernate.  So we can't change QueryOver (and IQueryOver) to Query & IQuery respectively.

This is why we originally had to come up with something else (i.e., QueryOver) in the first place.

While I suspect you're right that it's unlikely to change, you could always poll the user group for a more general response.  After all, NHibernate is owned by the community; so I'd feel duty-bound to change it if there was a strong response.

Have a great Christmas, and thanks again.


Dmitry wrote re: QueryOver in NH 3.0
on 12-22-2009 23:11

Can you have complex Where conditions such as

.Where<Person>(p => p.Name.StartsWith("Smith"))


.Where<Person>(p => p.Id == 1 || p.Id == 2)

Richard Brown wrote re: QueryOver in NH 3.0
on 12-23-2009 12:35

Hi Dmitry,

The first example requires use of a 'like' SQL expression, but you cannot use the StartsWith() method.  This would be written as:

.WhereRestrictionOn(p => p.Name).IsLike("Smith%")


.Where(Restrictions.On<Person>(p => p.Name).IsLike("Smith%"))

The second example looks fine.



Buthrakaur wrote re: QueryOver in NH 3.0
on 01-06-2010 11:56

aggregates aren't supported or do I miss something? I'd really appreciate standard aggregate functions support...

Richard Brown wrote re: QueryOver in NH 3.0
on 01-06-2010 14:13


You can do something like the following (in a similar fashion to ICriteria):


   .Select(Projections.Avg<person>(p => p.Age))


.. or using inline syntax ....



       .SelectAvg(p => p.Age)





Buthrakaur wrote re: QueryOver in NH 3.0
on 01-07-2010 8:19

great.. thanks a lot

maciejk wrote re: QueryOver in NH 3.0
on 01-16-2010 13:33

Looks great. However, SelectList / EndSelect syntax is a bit awkward to me. Why not use nested closure instead?



    .SelectList(list =>


                             list.Select(c => c.Name);

                             list.SelectAvg(c => c.Age);


Richard Brown wrote re: QueryOver in NH 3.0
on 01-16-2010 17:58

I like that suggestion.  Shouldn't be too much work either ... I'll see if I can squeeze it in next week.


wanda wrote re: QueryOver in NH 3.0
on 01-24-2010 19:52

Well documented and explained code. Thanks.

John Davidson wrote re: QueryOver in NH 3.0
on 01-29-2010 14:16

@maciejk, @Richard Brown

I just noticed this post and saw the proposed syntax. C# has support for a lambda expression tree as is proposed, but that is not yet available for VB users.

The original SelectList / EndSelect syntax would however work for both languages

Sometimes VB is not a choice, but a requirement! Hope you can reconsider this.

Rafał Kłys wrote re: QueryOver in NH 3.0
on 02-05-2010 6:23

As I understand it, QueryOver is an overlay on new possibilities which you introduced to ICriteria API, in such a way that you don't even need to use

1) session.QueryOver<Cat>().Where(c => c.Name = "Kitty"),

but end up with using

2) session.CreateCriteria<Cat>().And(Restriction.Where(c => c.Name = "Kitty").

If I'm true, I think that the name should reflect this, and be something like:

3) session.CreateQueryCriteria<Cat>()

Or better yet, IMO!, you should just make all possibilities available through

4)  session.CreateCriteria<Cat>().

This way we will have all the goods: familiar, old syntax and new powerful possibilities without adding something that is yet another way of querying and that looks almost exactly (!) like Linq:

5) session.Query<Cat>().Where(c => c.Name = "Kitty")

I believe that writing like in 2) is not that harder than in 1) that it justify itself to create a new API. And for those that will want that kind of API will have Linq.

And two more things

1. Why you use at the same time implicit and explicit interface implementation in the same classes in QueryOver.cs?

2. This one is completely irrelevant and a matter of taste:) . You have:


Maybe create something like:

Restriction.Like<Cat>(c => c.Name, "%anna%)

Rafał Kłys wrote re: QueryOver in NH 3.0
on 02-05-2010 6:25

It's really nice piece of work, btw.

Richard Brown wrote re: QueryOver in NH 3.0
on 02-07-2010 7:40

Hi jwdavidson,

Unfortunately I've never used VB.Net, so I'm unable to confirm which parts of the QueryOver API work with it.  There has also been discussions in the development group about what can/can't be supported with VB.Net:


As mentioned in the previous comments, if this becomes a highly voted for issue by the community, then I'm sure we'll readdress the problem.



Richard Brown wrote re: QueryOver in NH 3.0
on 02-07-2010 7:53

Hi Rafal,

Regarding points 1-4:

The QueryOver does not return ICriteria, but a typed interface (IQueryOver<T,U>).

The overload ISession.CreateCriteria<T>() already exists, and the decision to make the new API QueryOver was taken to specifically avoid clashes with the existing syntax.

While you can mix the new Restrictions overloads with the old ICriteria interface (as per point 1 & 2), you lose the implicit type that comes from the new interface.  Point 2 actually has to be written as:

session.CreateCriteria<Cat>().And(Restriction.Where<Cat>(c => c.Name = "Kitty"))

(Note the extra qualification of 'Cat' that is not required with IQueryOver).

On the second subject, point 1;  the explicit interface implementation is used because we have two overloads that only differ in return type (one returns QueryOver, the other returns IQueryOver).

And finally, I agree the last point is a matter of taste.  The inline restrictions become much easier to implement when each combination is not a separate overload.

Hope all that helps.


Buthrakaur wrote re: QueryOver in NH 3.0
on 02-12-2010 8:01

Is there any simple method how to set projections on QueryOver queries? I need to get count of entities returned by a QueryOver query and didn't find other method than using underlying CriteriaAPI like this:

var qry = customerRepo.QueryOver()

.WhereRestrictionOn(c => c.Name).IsInsensitiveLike(query.Name, MatchMode.Start)

.AndRestrictionOn(c => c.Address.City).IsInsensitiveLike(query.City, MatchMode.Start);

var countCrit = (ICriteria)qry.UnderlyingCriteria.Clone();


res.TotalItemsCount = Convert.ToInt32(countCrit.UniqueResult());

Buthrakaur wrote re: QueryOver in NH 3.0
on 02-12-2010 9:23

sorry for my blindness :)


SynC wrote re: QueryOver in NH 3.0
on 03-01-2010 13:43

Hi Richard, help please.

1. Can be done something similar:


   .SelectList(list =>


                            list.Select(c => c.Price * c.Count);

                     }); ?

2. What about using anonymous types for projecting?

Richard Brown wrote re: QueryOver in NH 3.0
on 03-06-2010 20:25

Hi SynC,

1.  QueryOver is built on top of the ICriteria API, and I'm not sure you can do that projection in the ICriteria API (I could be wrong though);

2.  Fabio suggested the same thing.  I tried projecting directly into anonymous types, something like:

.Select(o => new { o.Price, o.Count});

I couldn't get it to work;  I can't remember the exact problem I had, but it was something to do with the parameters to the anonymous function.  Also, I'm not sure if the syntax would execute the query at this point, or still return an IQueryOver (to then call List(), or SingleOrDefault().)

I might try again sometime when I've got some spare time.

Oh, and patches are always welcome too.  ;-)


technology essays wrote re: QueryOver in NH 3.0
on 06-28-2010 5:58

@maciejk: Thanks for the suggestion. I did play with the code you shared. I was able to run it flawlessly. Thanks again

Richard Brown wrote re: QueryOver in NH 3.0
on 06-28-2010 7:49

Note, the syntax changed slightly recently - it's now .SelectList(list => list...)

(to avoid clashing with intellisense)

Context is King wrote NHibernate 3 and Autofac, can go together? (part 1)
on 10-05-2010 4:56

In this post, I intend to explain about NHibernate and integrated it with Autofac . And some content

Yesterday's news wrote What's new in NHibernate 3.0 GA
on 12-04-2010 23:07

What's new in NHibernate 3.0 GA

gt124 wrote re: QueryOver in NH 3.0
on 12-29-2010 18:47

What an awesome feature, I don't have performance issues yet, so I basically used to use Criteria for everything and Ayende's filter pattern all over the place.

However, I just love queryover syntax and I need more documentation please. It was a supreme pain figuring out how to do a "NOT IN" yesterday, if you are looking, use a WhereRestrictionOn instead of where.

Yes, I guess I should write it myself, but with a job and two kids, I'm hoping one of the younger super brains can do it for me :)

Have a heart!

danleyvilla wrote re: QueryOver in NH 3.0
on 01-13-2011 15:42

I've tried looking through the source but it doesn't really explains it fully to me.

How do they make them type safe, at compile time, without the need for any proxy classes that extends them?

I would like to "copy" this behavior but for that I need some basic understanding of the concept, any pointers to documentation on the subject is also welcome.

<a href="www.eurodebt.com/">debt solutions</a>

Rafał Kłys wrote re: QueryOver in NH 3.0
on 01-16-2011 8:15

For a long time I didn't understand the need for QueryOver. But lately I used for the first time NH LINQ, and after some time I thought:

Well, Linq is nice, but I don't like the transparent translation from database query into in-memory query. I would like something like ICriteria, but strongly typed. Oh, there is such thing, QueryOver!


Pluralcast wrote Pluralcast 34 : NHibernate 3 with James Kovacs
on 01-17-2011 17:27

  Listen now ! [48:11] NHibernate 3 has released with some great new features including a LINQ provider

Hainesy wrote re: QueryOver in NH 3.0
on 03-01-2011 12:10


I know this is an old post but this took me ages to work out... your two examples showing projections with Avg for cat age, the selects should be SelectGroup, as shown in your AliasToBean example.

Hainesy wrote re: QueryOver in NH 3.0
on 03-01-2011 12:21


I have a problem using Cacheable() after .TransformUsing(Transformers.AliasToBean<T>())

I get the error:

Unable to cast object of type '...' to type 'System.Object[]'.

Seems that cacheable doesn't work with the bean transformation?

papichullo wrote re: QueryOver in NH 3.0
on 09-30-2011 8:43

You covered this topic well. It helps a lot of newbies out there. Looking for more....

Simon Taylor - .Net developer, Cheltenham wrote Reading from the database with NHibernate
on 12-01-2011 10:02

Reading from the database with NHibernate

usatoryburch wrote re: QueryOver in NH 3.0
on 01-13-2012 9:59

Hi! I found your blog on AOL.It’s really comprehensive and it helped me a lot. Continue the good work!

Powered by Community Server (Commercial Edition), by Telligent Systems