Friday, April 16, 2010

Moving Suteki Shop from Linq-to-SQL to NHibernate

I’ve finished getting the basic features of Suteki Shop running with NHibernate. Anyone who’s been following this project knows that it was originally written with Linq-to-SQL. Linq-to-SQL worked fine for the initial iterations of Suteki Shop, but there are lots of things I wanted to do with the project that would have been hard or impossible to do without the move.

Linq-to-SQL not a true ORM in the sense that it doesn’t support persistence ignorance, it’s more a object based projection of a relational database. Each class directly represents a table row, including foreign keys. One of the main pain points in the transition was removing all the application code that referenced these foreign keys and replacing them with object references instead. So for example, instead of changing the int value of Contact.CountryId I would change the actual referenced entity, Contact.Country.

The other place where I had to do lots of work was in my Linq queries. The NHibernate trunk now has a native Linq provider written by Steve Strong, so you can write:

var customer = session.Query<Customer>().Where(c => c.Name == "Fred").FirstOrDefault();

The main difference between the Linq-to-SQL provider and the NHiberante one is in the way they treat expressions that they don’t understand. Linq-to-SQL will translate as much of the expression that it can to SQL, get the result, and then allow Linq-to-objects to do the rest. The NHibernate provider will throw an exception saying that it doesn’t understand the expression. There are pros and cons to both approaches. Linq-to-SQL will always give you what you want, but maybe in an inefficient way you weren’t expecting, whereas NHibernate will force you to think about how you structure your queries. There are no surprises, but it’s a mark against persistence ignorance. Right now, I’m liking the NHibernate no surprises approach.

Whilst doing the conversion I ran into a problem with the OfType<T>() Linq extension method. I was using it in several places but NHibernate complained that it didn’t support it. I tweeted about it and almost immediately Steve Strong replied that it should be easy to implement. The next day he committed it to the NHibernate trunk and my queries all worked.

steve-strong

Steve Strong, you are an awesome open source hero!

One of my major regrets with the way that I originally wrote Suteki Shop is binding views to entities, rather than using a view model. At first this can seem like an easy-win, but doing this work showed how can soon lead to all sorts of difficulties. Displaying the view model in the view is not so much of a problem - except in entity loading overhead. The problems mostly manifest themselves at the binding stage. I extended the ASP.NET MVC DefaultModelBinder to work with NHibernate entities, and it mostly works, but I spent far too much time resolving tricky binding issues with complex object graphs. I would have made my life much easier by binding to a nice flat view model and then manually mapping that to my entities.

Persistence ignorance is a dream, a nirvana, where we’d love to be, but no ORM will give you true persistence ignorance. NHibernate is far better in this regard than Linq-to-SQL, but you still have to do things the NHibernate way. Your ORM will hugely influence the way you build your application and although the persistence ignorance ideal should mean you can swap them with little difficulty, moving from Linq-to-SQL to NHibernate was a major piece of work, far more than I expected.

2 comments:

Unknown said...

Haha, sorry to interrupt your twitter conversation. I'm right in the middle there.

Pradeep Gururani said...

Hi Mike

Did you evaluate Entity Framework 4.0 before closing on to NHibernate? If yes, what were the reasons which forced you to not to go for EF?