Cuckoo for POCO Puffs: NHibernate edition

Migrating from EF model-first to code-first with FluentAPI wasn't terribly difficult and resulted in far simpler code.  Once completed, the resulting POCO's were pure enough to appear to be compatible with NHibernate.  The question I wanted to answer was how similar Fluent NHibernate is to EF’s code-first FluentAPI in terms of how it managed those POCOs.  What I was ultimately looking for was some kind of definitive answer that demonstrated that they were either similar enough as to make no difference or dissimilar in ways that made one clearly superior to the other.  The most thorough and recent comparison I had seen was Dino Esposito's in 2010, and that wasn't definitive nor was it necessarily tailored to our particulars.

Hey my man, what it look like?

What I found was that the model-first to code-first conversion was a much bigger switch than from code-first to NHibernate.  The resulting NHibernate implementation looked nearly identical to the EF code-first FluentAPI version. There were a few syntactic differences in the FluentAPI.  With NHibernate I had to use ".Not.Insert().Not.Update()" in cases where I had both a navigation property and a foreign key Id so that the insert/update statements didn't duplicate columns.  The Linq provider implementation was slightly different too.  Instead of DbSet properties of the context NHibernate uses a generic query method (session.Query<Myobject>()), and instead of Include() NHibernate uses Fetch() and ThenFetch().  I think anyone would agree that these are pretty minor differences.  From the perspective of the resulting code there was very little difference in complexity, organization or syntax.  For my humble purposes and based on this somewhat shallow analysis, it looks like they are similar enough as to make no difference.

The secret awaits eyes unclouded by ambition

Beyond the code I did discover some noteworthy differences between NHibernate and EF.  EF doesn’t have a built in mechanism to trace the generated SQL, which seems like such an obvious basic feature.  I overcame this by using extension methods before, but this is only a partial solution.  It will only trace SELECT statements, not the INSER, UPDATE and DELETE’s and it doesn’t include parameter values.  I took a look at EFProviderWrappers and wasn’t encouraged.  Its mainly sample code not an actively maintained project, it requires some odd configuration tricks, and doesn't support code-first’s DbContext.  While I could adapt this sample for my purpose, it really shouldn’t be necessary.  NHibernate will output the generated sql with a simple ‘ShowSql’ configuration setting.  Its not hard to hear the claimed lack of maturity and extensibility points in EF ringing true when the first, most basic one, is a hassle.

Those who are bound by desire see only that which can be held in their hands

On the flip side, I found that NHibernate dependencies to be fragile.  I downloaded the Fluent NHibernate 1.2 version which included Nhibernate 3.1.  Immediately upon running the application I began encountering a series of dependencies issues.  The two most troublesome of which were:

Could not load file or assembly 'NHibernate.ByteCode.Castle' or one of its dependencies. The system cannot find the file specified.
The type initializer for 'NHibernate.Cfg.Configuration' threw an exception. The invoked member is not supported in a dynamic assembly

I solved the former by adding it as a reference to every project that referenced the assembly that referenced NHibernate.  I don't find that to be a particularly satisfactory solution.  The latter was resolved by upgrading my other assemblies to use log4net 1.2.10, another odd nuisance.  While searching for answers to those two particular issues I discovered a frightening number of other compatibility woes plaguing others.  A well documented Microsoft supported and integrated Entity Framework requires far less fuss and googling.

You just get that sucker to the designated place at the designated time, and I will gladly designate his ass... for dismemberment!

So far, there’s not much to fight about.  Either I have to do a little extra work to get tracing, or a little extra work to make sure my versions and dependencies are straight.  There are, however, some more strategic considerations. 

  • NHibernate’s Level 2 cache is dreamy, the EFProviderWrapper is pitiful.  Until Microsoft or a third party writes a commercial Level 2 cache for EF, NHibernate has the advantage, with a caveat.  I’m not sure how well the Level 2 cache works or is to work with in practice, but more importantly the likelihood of our data access being all or mostly managed by an O/RM is a long way off, which somewhat devalues its usefulness in the short term. Still I can’t help but be enamored by the possibility.

  • Entity Framework is the preferred data access technology from Microsoft. That means documentation, support, integrated tooling, potentially wider adoption.  Its not hard to imagine EF reaching feature parity and even surpassing NHibernate in the near future, other than the fact it should have already done so and still has a ways to go.  The more troubling aspect of the “it comes from Microsoft” double edged sword is that to get new EF features we had to upgrade the entire platform from .NET 3.5 to .NET 4.0.  I’m hoping we don’t need .NET 4.5 to get EF 5.x features.  NHibernate is somewhat freer in this regard to add features independent of the full framework.

Well, I did you one better. I mastered "the art of fighting... without knowing how to fight".

Objectively, I might give the slight edge to NHibernate, all things being equal.  But a slight edge isn’t quite enough to go off into unchartered territory with NHibernate just yet.  I think we’ll continue down the path of converting our EF v1 style model-first code to Entity Framework code-first with FluentAPI (I’m still not sure how to refer to it), while taking comfort in the fact that as long as our POCO’s are pure its not a decision that is irrevocable.


Popular posts from this blog

RIF Notes #42

RIF Notes #4

RIF Notes #1