Ayende @ Rahien

Hi!
My name is Ayende Rahien
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

@

Posts: 5,947 | Comments: 44,543

filter by tags archive

NHibernate – query only properties


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.


Comments

Benny Michielsen

I had no idea this was possible, keep 'em coming!

Dave the Ninja

@Benny - neither did I!!!

@Ayende - Time to refactor some code written last night to do this very thing - time to remove the noisey POCO collection reference!

CHEERS!!!

Ninja

Krzysztof Kozmic

I imagine using this this will be tricky to implement with LINQ (or strongly named criteria) which assume that Entity Model == POCO model

Shane Courtrille

The world would benefit greatly from a series titled..

"Things you didn't know about NHibernate"

This is awesome :D

Gunnar Liljas

It can be implemented with LINQ if you're not afraid of exposing primary and foreign keys as properties. Not beautiful, but it's actually just a bit of noise.

Stephen

I guess technically you could have entity types and linq against them.. very hacky though..

Ayende you say: "In our domain model, we don’t want to have a Blog.Posts collection, we want to only have a Blog.Post."

Did you mean Post.Blog? otherwise I don't follow..

And can you explain why you wouldn't want Blog.Posts on the poco? just the example scenario, or good practice? is this related to aggregate roots?

Ayende Rahien

Yes, that was a typo.

And there are a few reasons, most of them relating to the fact that you don't want to have highly connect object models, or that if you would try to create those associations, you would have association explosion

Jose

is this working?

select b.Id,

b.Name,

size(b.RecentPosts)

from Blog b

where size(b.RecentPosts) > 5

Min Han

These recent posts on NHibernate are really interesting. I would have no idea how I would have come upon this particular gem without this post.

Richard Dingwall

I didn't know this was possible either, very cool. It looks like it works for version columns too (who cares abouts optimistic locking implementation in the domain model).

I even tried it on an ID column (value type in domain, but has an ID in the legacy relational schema), but that failed with this exception:

NHibernate.HibernateException : identifier of an instance of SMS.Domain.Osm.CoreCompetencyRequirements was altered from 12189 to

:O might log a bug report

Michael Teper

Hmm, that's really interesting, but only as a curiosity. It actually looks like an antipattern to me because the mapping, which is supposed to be an implementation detail is now required to understand query syntax, in addition to the POCO model.

Richard Dingwall

Hmm check that. Noop'd version is not working either.

Gerke Geurts

@Michael Teper: If query implementation is the responsibility of a repository/query service, just like the mapping definition, then I don't see any design conflicts.

Frans Bouma

I really don't understand why anyone would want to add query predicates to a mapping file in XML and use that in a string based query in C#. If something will become a maintenance nightmare this is probably it.

Not having a field mapped onto a relationship is not that uncommon, but mainly in o/r mapper frameworks which do reverse relationship maintenance (i.e. myOrder.Customer = myCustomer; also adds myOrder to myCustomer.Orders), as you sometimes don't want the PK side to know about all the FK sides it has, for example because it's kept in memory and could lead to problems.

As the other side of the relationship does have a field mapped (Post.Blog), isn't that enough to query?

Paul Batum

Hmm, I didn't know you could do that. Not sure how we'll support this one in Fluent NHibernate...

Hendry Luk

@Krzysztof,

It "could" work in strong type criteria, eg NHQG. Doubt it's there though atm.

Denis

You say that "there are a few reasons" for not exposing Blog.Posts.

I'm wondering whether one of this reasons is that such one-to-manymany relationships are "dangerous" with regard to performance. Hiding them can reduce the risk of developpers using them and forgetting what happens under the hood. Is it the case ?

Ayende Rahien

That is one of them, for sure. There are also implications from the point of view of the domain model, but mostly it is because a lot of those associations just doesn't really make sense as an association on the objects

Chris Nicola

Interesting, though I may be a bit confused as to the difference between the entity model and the POCO. The POCO is the domain model I take it, but then what is the nature of the mapping from entities to POCOs?

Also, if I were creating a UI using MVVM does that mean I would have yet another mapping of the viewmodel to the POCOs. Seems overkill perhaps, but it is more than likely I am just misunderstanding. I just don't want to get into the mistake of creating redundant code.

Ayende Rahien

Chris,

That is why this is hidden very deeply inside NHibernate, where you don't usually see it.

From your perspective, you can treat POCO as the final model

Wayne Douglas

I think of this as saying this:

in the EM that we want to have a virtual property mapped one-to-many to another entity. The POCO has no concept of the relationship - only the entity model expressed through config?

I think I get all the concepts here and it's definitely very cool - what I'm struggling with - as were a couple others is why Blog.Posts is a bad idea - seems like the most natural thing in the world to me :s

Is this because, semantically, you think it's wrong to have this relationship?

Cheers

w://

Chris Nicola

Ah I understand what you're saying now. This is definitely a useful feature I can imagine a number of situations where I could use it.

I feel like I may have made a mistake with my bi-directional associations though. I have a sort of three level tree of one-two many associations where I query the root entities and then load the collections.

Ayende Rahien

Wayne,

Don't look at Blog.Posts, try to think about Blog.RecentPosts, or Blog.PostsByCurrentAuthor, or stuff like that.

Alexander Kovtik

Does noop access strategy work with simple properties (not collections)? I can't get it working...

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. RavenDB Sharding (3):
    22 May 2015 - Adding a new shard to an existing cluster, splitting the shard
  2. The RavenDB Comic Strip (2):
    20 May 2015 - Part II – a team in trouble!
  3. Challenge (45):
    28 Apr 2015 - What is the meaning of this change?
  4. Interview question (2):
    30 Mar 2015 - fix the index
  5. Excerpts from the RavenDB Performance team report (20):
    20 Feb 2015 - Optimizing Compare – The circle of life (a post-mortem)
View all series

RECENT COMMENTS

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats