NHibernate – query only properties

time to read 4 min | 727 words

With most applications, there is some difference between the domain model and the data model. Probably the most common scenario that I run into can be expressed in the usually Blog & Posts example. In our domain model, we don’t want to have a Blog.Posts collection, we want to only have a Post.Blog.

However, in the data model, all associations are bidirectional, but that doesn’t mean that we want to have the same in the domain model. The problem is that we still want to query on that. That is a bummer, right? Because now we have to change our domain model to fit the query model that we want.

Not really. It is not well known, because we don’t generally think about this as an important concept that should be externalized to the users, but while we usually portray NHibernate working on the following model:

image

This is not actually accurate. What is closer to the way NHibernate works is this model:

image

And the reason that this is important is that the entity model doesn’t have to match the POCO model.

Let us see how this works:

<set name="Posts"
     access="noop">
    <key column ="BlogId"/>
    <one-to-many class="Post"/>
</set>

As you can see, we specify a no op access strategy (with NH 2.1 you can also use access=”none”, which sounds nicer), to do so. Now, we can execute a query:

var blog = 
    s.CreateQuery("from Blog b where size(b.Posts) > 5")
    .List<Blog>();

Remember, Blog doesn’t have a Posts property, it is a query only property. This code results in:

image

This is actually most useful when you want to have different projections on the same thing. Let us take a look:

<set name="RecentPosts"
     where="(PostedAt >= (getdate() - 30) )"
     access="noop">
    <key column ="BlogId"/>
    <one-to-many class="Post"/>
</set>

And using this just as before:

var blog =
       s.CreateQuery("from Blog b where size(b.RecentPosts) > 5")
       .List<Blog>();

And that will result in this:

image

I think that this is the key usage scenario for this feature, being able to create query only associations that I’ll traverse only for querying.