A place to discuss Development techniques, .NET, XNA, NHibernate or anything else that tickles your fancy

Thursday, July 16, 2009

Video: Fluent NHibernate session from Chicago's Alt.NET

Sergio posted the video of my session from 7/8.
http://chicagoalt.net/event/July2009Meeting060withFluentNHibernate

This one was a good bit longer than the one from Chicago Code Camp, due to no hard time restrictions. Thanks Sergio!

Thursday, July 9, 2009

NHibernate Tip #3 - ID's

(Prequalifier, this is still a subject of much Debate, please toss in your thoughts into the comments. These are Tips that I've run across that I wanted to share, it's not to say that this is the best way to do something every time, and should not be considered the catch all solution)

A subject of much debate, especially if anyone in the debate happens to have anything resembling a background as a DBA. I'll come right out and say what I believe to be the most ideal solution in a non-legacy environment that isn't geared towards squeezing the most performance out of the database as possible:



   1:  <id name="Id" 

   2:          column="PrimaryTableID" 

   3:          type="System.Guid">

   4:        <generator class="assigned" />

   5:  </id>

   6:  <version name="Version"

   7:               unsaved-value="0" />




I would also recommend to have all of your objects generate the ID in the non-default constructor (you typically want to leave an empty default constructor for NHibernate for it to load an object back from the database without any "Object initialization" logic)

The above mapping is, in my opinion, the most flexible ID setup to use with NHibernate. Why?
  • NHibernate never has to go back to the database to "find out" what the ID is for the object, which is a very expensive operation on an Identity generation setup. A Hi-Lo setup would be a decent second choice to address this concern
  • Assuming you generate the ID in the non-default constructors, the ID of an object created anywhere is going to be consistent, whether you're outside of a session (such as in model tests), or in a session using hydrated objects
  • It significantly simplifies the equality checks you're required to do. As someone commented in my previous post, if you're unable to rely on having a unique ID at all times, your equality logic has to start incorporating additional rules to account for the pre-persistance case scenario's
  • Version manages when an object should be inserted through a cascade or not (as opposed to an unsaved value attribute). Version is also handy to have for other reasons that I'll bring up in a later post.

A couple of cons:
  • Looking at Guids in the database is not a pleasing experience, but really, you shouldn't be interacting with the database at that level. It's an implementation detail.
  • Using Guids in the database can increase DB fragmentation, but really, that's where I feel the Database Engine Tuning advisor (if you're using MsSql) or a DBA steps in. There are ways around this, and as far as we've been able to tell with millions of rows in a production application, the differences seem to be almost un-noticable. Still, there's no way to get around it, it's not the best performing scenario on the DB side.

To be honest, I was originally on the side of continuing to use Identity, although I was looking at a hi-lo generator algorithm with it. My background is in a more DBA-ish field, so the concept presented above was like garlic to a vampire.

Times, they be a-changin. We need to let go of some of our old DBA habits in favor of emerging frameworks/concepts which can hide persistence complexities.

Wednesday, July 8, 2009

NHibernate Tip #2 - Equality Overrides

Override Equals, GetHashCode, and the !=/== operators - This is pretty basic. If you're using NHibernate, as a general rule of thumb for a best practice, you should override all of these functions. Why? Because by default your applications will attempt to do a referential check to determine if they're equal, or if the object already exists in the collection. Implementing your own routine allows you to check the business properties of an entity, such as the ID or business data, to determine equality. To put it simply, NHibernate won't work correctly if you don't do this.

Overriding !=/== is important when you've set your associations to lazyload, because NHibernate will generate proxies for those objects to support the lazyloading concept. Those proxies rely on these two overrides to determine object equality based on business rules (again, such as the ID/business rules).

Also important to note, testing the ID property of a lazy loaded (proxied) object does not load the object from the database. The ID property is free, that's how the proxy knows about which entity to load from the database. In an ideal world, you should try and work all of your equality rules to use the ID so you can take advantage of all of NHibernate's optimizations.

Tuesday, July 7, 2009

NHibernate Tip #1 - Lazy Loading

I wanted to jot down all of the NHibernate tips and tricks that I've learned over the last year of using this thing in production, so this is everything I can remember up to current. Every day until I run out of things I've encountered, I'll be publishing another tip.

Lazy Load Everything - Unless working in a remoting type of environment where you're not guaranteed a session while working with the objects, it's best to let this default to true for all of your entities. I've been burned by this hard, there's a fundamental misunderstanding that I had when starting to pre-optimize (also a bad idea). It went something like this: If I don't lazy load an entity, my application will perform better, because it will have eagerly fetched the data that I will use 80% of the time.

The problem is that lazy loading does not eagerly fetch anything for you. All it does, is execute the same SQL statement to get the object, that it was going to when you eventually attempted to access it, except it's going to do that 100% of the time now. Setting this option to false does *not* improve performance in any way, in fact, in every case, it will actually degrade performance. This is a feature, that as far as I can tell, should only be changed to false when you've got an object graph that loses scope from the session (such as in remoting).

What most people are looking for when they set this to false on a mapping, is the "fetch=join" attribute. This will include the associated entity in the same SQL statement used to get the originating entity. I'll probably make another post about the join fetch, all I can say for now is to use that sparingly. It can improve performance, but should be set at a more granular level (A Criteria or HQL query), as opposed to the entirety of the application.