Wednesday, December 19, 2012

NHibernate WCF Error: HTTP request context being aborted by the server??

I’ve been ambivalent toward ORMs for quite some time now, but I’m really starting to dislike NHibernate in a special way.  My experience has been that ORMs lead to a lot of wasted time troubleshooting code problems that are really ORM problems.  Here’s one example.

I recently got this mysterious and unhelpful error message on an ASP.Net MVC3 web app that was pulling data via a WCF web service, which in turn was using NHibernate for persistence.

An error occurred while receiving the HTTP response to http://localhost:8080/JobsService/ws. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.

Ok, my service endpoint was definitely using HTTP and none of that other stuff was happening either.  So what was the problem?  NHibernate lazy load proxies.

Let’s say I have a Company entity that contains a child collection of type List<Job> as shown in the diagram below.  So if we look at the Company entity, all of the fields (Name, State, City) exist in my data table, except for the Jobs.  Jobs represents a relation between my Company and Job tables and to get Job data NHibernate has to query the Job table.

image

At this point it’s important to note that I’m using NHibernate, all of my entity properties are virtual, and I have lazy loading enabled.  So what happens when I run a query to get a Company? What data does NHibernate populate?  It’s going to populate all of the data that’s in the Company table (Name, State, City) , but it’s not going to populate the Jobs collection.  It’s going replace that virtual property with a proxy that will execute a query to get Jobs only when that property is accessed. 

I actually think this lazy loading proxy technique is pretty neat and it’s technically impressive.  Unfortunately it’s also the root cause of a lot of application errors.

So the lazy loading proxy works great when we’re accessing an entity directly in our web app while it’s still has the context of the NHibernate session, but what happens when we try to return our Company over WCF and WCF tries to serialize that Jobs property that hasn’t been lazy loaded yet?  That’s right, you get the ambiguous error message above. 

The fix is simple.  You just need to eager load the Jobs. You can do this any number of ways.  Here’s how to do it in a criteria query.

return session.CreateCriteria<Company>()
            .Add(Restrictions.Eq("Id", _Id))
            .SetFetchMode("Jobs", FetchMode.Eager);

Now that we’re eager loading the Jobs there’s real data there instead of the lazy load proxy, WCF has no problem serializing the Jobs, and our mysterious ambiguous error goes away. 

So, I hope this saves someone a little time, and please remember, friends don’t let friends use ORMs.