NHibernate new featureNo proxy associations
About three weeks ago I introduced the problem of ghost objects in NHibernate. In short, given the following model:
This code will not produce the expected result:
var comment = s.Get<Comment>(8454); if(comment.Post is Article) { // }
You can check the actual post for the details, it related to proxying and when NHibernate decides to load a lazy loaded instance. In short, however, comment.Post is a lazy loaded object, and NHibernate, at this point in time, has no idea what it is. But since it must return something, it returns a proxy of Post, which will load the actual instance when needed. That leads to some problems when you want to down cast the value.
Well, I got fed up with explaining about this and set about to fix the issue. NHibernate now contains the following option:
<many-to-one name="Post" lazy="no-proxy"/>
When lazy is set to no-proxy, the following things happen:
- The association is still lazy loaded (note that in older versions of NHibernate, setting it to no-proxy would trigger eager loading, this is no longer the case).
- The first time that you access the property the value will be loaded from the database, and the actual type will be returned.
In short, this should completely resolve the issue.
However, not the key phrase here, like lazy properties, this work by intercepting the property load, so if you want to take advantage of this feature you should use the property to access the value.
More posts in "NHibernate new feature" series:
- (28 Jan 2010) No proxy associations
- (27 Jan 2010) Lazy Properties
Comments
This is great! I know that it violates Liskov and such, but so many new/junior programmers get bitten by this when they first introduce inheritance in their model.
Really Great, but do you recommend use it in all many-to-one associations? Is there a rule?
Why not load the type of the associated object during the initial query (might need to add some joins depending on the inheritance storage strategy chosen) so that a proxy of the correct type can be generated ?
No, I don't.
If you can, keep the current behavior. This requires NH to do more work and it encourage bad design practices
Steve,
You can do that if you want to eager load.
But in some cases, you want to do things lazily.
I mean, NH could automatically pre-load to the entity type of lazy many-to-one association.
If I do a comparison 'comment.Post.Id == 123', you know it better than me, Post is not going to be lazy-loaded because the proxy holds the Id. Similarly, I would expect lazy-loading not to be triggered if I do 'comment.Post is Article'.
Steve,
That implies eager loading, the same cost is associated with this or with eager loading.
Just out of curiosity (I'm not a NHibernate-user). The Comment.Post-property has to be virtual in order to allow this behaviour, right?
Gamlor,
In general, all properties/methods must be virtual.
To rephrase Cassio Tavares' question:
Do you recommend to use it in all many-to-one polymorphic associations?
Martin,
Again, no. Use it if you need it, but I don't like needing it
Alex,
I still don't agree with your reasoning.
And it takes a single query.
Ah, yes... Surely there is a single query when yo materialize a proxy. But what happens if you access a property of inherited type later?
Concerning the reasoning - well, deeds say more than words.
Sorry, wrong description, I mean "when NH resolves the reference".
Alex,
If you access the property, there is no query.
re: deeds, there are things that I do because people want it enough, not because I think it is good
Good answer ;)
So may be I'm missing something. While doing the first query, you actually don't know the exact type of referenced entity (of course, if there is inheritance hierarchy with type discriminator), and thus you don't know how to completely fetch the instance.
Querying all the tables from the hierarchy is, likely, a bad idea as well, because there can be lots of them.
So how such case is handled?
When you access the property on the root entity, we load the value (one query).
Let's imagine we have Person.Pet reference of Animal type, and the instance it refers can actually be of type Cat, Dog, ... any other of its 20 inheritors (so the hierarchy is mapped to 20 different tables). And we use class per table inheritance mapping with type discriminator.
Let's think I run the following code:
var person = ...; // Person instance is loaded
var pet = person.Pet; // Animal instance is loaded, but in which part?
var dog = pet as Dog;
if (dog!=null) {
double barkVolume = dog.BarkVolume; // Any DB interaction?
}
Alex,
var person - db query
person.Pet - db query.
That is all
Nice! just a question: all these new proxy/lazy loafing new features are here because hibernate has them or just because they are really good? The proper question should be: is nhibernate still following hibernate's steps?
Ivos,
NHibernate still follows in Hibernate footsteps, but is not limited to them
Which tables will be used by this query in described case?
Alex,
Similar to what would happen if I did:
session.GetPet
If I understand everything correctly, earlier session.GetPet would return a proxy, which type isn't dependent on actual Pet type there (i.e. won't be able to successfully cast it to e.g. Dog).
The query that would run in this case fetches just Dog's fields, so this line must lead either to DB interaction, or to unexpected result:
double barkVolume = dog.BarkVolume; // Any DB interaction?
But since you describe new functionality, it's desirable to describe what's changed in this case.
I mean "I won't be able to ..."
Sorry, "Pet's fields."
Alex,
Get does NOT return a proxy.
Now it's clear that your statement about session.Get was correct. I tried to find out what actually NHibernate does to load an instance mapped with "joined-subclass" strategy, and actually failed. I see two basic options here:
a) Do this using a single query involving all the tables in the hierarchy ( or b) a part of them, if entity type is partially known - e.g. as T generic argument of session.Get method)
c) Query for hierarchy root type, detect actual entity type and run a precise query involving just necessary tables.
So what algorithm is used by NHibernate in this case? A, b, c, a combination of some of them, etc.?
Take a look here.
ayende.com/.../...e-mapping-ndash-inheritance.aspx
Thanks for a link - there is really a perfect example.
So just to confirm: does NHibernate always use way a) ?
Alex,
To perform polymorphic query, yes
Ok, thanks. Thinking on consequences...
Comment preview