Friday, April 25, 2008

NHibernate errors: 101

We seem to be having a bit of trouble with NHibernate of late. Below are some basic check and strategies to get the most out of the ORM. This document is very basic but covers 90% of the issues I have had to fix with NHibernate on this project. For some background i believe I am the only one to have work with NHibernate previously and had come in well after the DAL had taken shape, so some basic mistake have been uncovered as part of our learning process.

Basic checks
Check Mapping files are embedded resources (right click the file in VS and select Properties. Ensure the build action is Embedded Resource)

Check the class is marked as Serializable

public class Customer : DomainEntity<int>, ICustomer

Check the properties are Virtual

public virtual Orders Orders
return this._orders;
this.orders = value;

Lazy loading exceptions

Check the object loaded is occurring in a session correctly and the session is being properly closed & committed
Check the mapping for the object and the children you are trying to load. If the Mappings are set to lazy=true, you will have to explicitly load them or access a property on the object to hydrate the object graph to the next lazy node down the object graph.
Check your session is not closing prematurely, either by code or by an internal error such as DB timeouts. Nested session will all be committed at the committing of the upper most transaction. Be wary of overly nested transaction as this may lead to time out and a possible “number of connection/cursor” exceptions throw from the database or it drivers.

Concurrency Issues
NHibernate is finicky with objects being loaded multiple times and associated in different sessions. This (in my cases) has usually come down to poorly factored code. E.g. Make sure you are not trying to put the same object in to a collection twice from 2 different sessions etc. I don’t have any great advice here as this is usually handled on a case by case basis, but is a good sign you need to rethink your design.

Rules of thumb
Reference data should be cached, immutable and eager loaded. This makes the pretty basic assumption that no relationships exist off to other objects (i.e. no children or other mapped entities)
Complex domain data should generally be lazy loaded, immutable and not cached. You can set up lazy/eager loading strategies on a per property basis. Eager loading simple types is fine, be careful with complex relationships as you should be catering for the most minimal requirements. Excessive use of eager loading will severely affect performance.
Again, be careful with eager loading. If you eager load a child there may be any number levels of eager loading down the object graph. Eager loading should generally be avoided unless there is a business reason to do so. Instead explicitly load each object or object collection explicitly in the business code that deals with the situation, ie

//this occurs with in a session to get all the Orders of the given customer

this.TheOrderDao.Initialize(customer.Orders); //Using NHibernateUtil.Initialize

Preferentially walk the object graph as opposed to using find by id or overly simple HQL. I.e. to get the customer orders use customer.Orders as opposed to a query that looks for order based on a customer ID. NB: This must be done in-session if objects are marked as lazy.
Use ICriterion styled searches to do simple object searches, ie: Find all customers with city location of London
Use HQL queries to get type objects back from queries. These are general object based queries where a collection of a mapped type is returned but may need other joined tables for querying purposes. Ie return all the product categories for all the orders of customer x for the last 365 days. These queries can usually be though of as follow the format select customer.* from customer inner join….
Only use stored procs for set based queries to
aid in speed if walking the object graph would be extraneous or load too much redundant data
to return streamlined DTO/DataSet for non type queries or queries that would otherwise return overly large object graphs when only a small portion of the graph is required. (These are the exception rather than the norm.)




Lee Campbell said...

Do you find that using Hibernate with what appears to be some fiddly maintenance issues to be useful?
I mean how hard is to write an SP and call it in .NET?

RhysC said...

As you know I generate my DAL for personal work, here at "work" work we dont, which leads to fiddly maintenance, and lots of human error.*

I see benefits in NHibernate: concurrency, lazy loading, caching, mutablity, etc all out of the box. Plus you can use SP's and HQL too. This is a benefit as it means you can keep devs out of the DB. I just dont trust most developers in the database, the database is still considereda second class citizen by most developers, which is insane.

For me to create a basic DAL layer that does all the basic CRUD stuff with nhibernate using a repository pattern, and leveraging my existing Core NHibernate base libraries takes about 1 minute (this should be check in to SV this week, templates and asseblies).

What I do have to manually do is drop these into the respective projects, embedd the xml maps. so that takes another 2 minutes.

At work, for us to do the exact same ammount of work would take a couple of weeks. This for me is not such a bad thing because i get a deeper understanding of nhibernate when fixing bugs.. so i get paid to learn a framework. I'm fine with that ;)