This is a question that I get routinely, both from random strangers and when I am at clients.
I would like to design a system/application using NHibernate. But I also want to so flexible that in future ,if I unplug the NHibernate and use ADO.NET Entity framework or other framework then my application should not
In short, I am completely opposed for even trying doing something like that.
It is based on flawed assumptions
A lot of the drive behind this is based on the historical drive built in the time where data access layers directly accessed a database using its own dialect, resulting in the need to create just such an encapsulation in order to support multiple databases.
The issue with this drive is that it is no longer a factor, all modern OR/Ms can handle multiple databases effectively. Moreover, modern OR/M are no longer just ways to execute some SQL and get a result back, which is how old style DAL were written. An OR/M takes on a lot more responsibilities, from things like change tracking to cache management, from ensuring optimistic concurrency to managing optimal communication with the database.
And those features matter, a lot. Not only that, but they are different between each OR/M.
It doesn’t work, and you’ll find that out too late
The main problem is that no matter how hard you try, there are going to be subtle and not so subtle differences between different OR/Ms, those changes can drastically affect how you build your application.
Here are a few examples, using NHibernate and EF.
Feature NHibernate Entity Framework Futures Yes No Batching Yes No Transaction handling Requires explicit code Implicitly handled Caching 1st & 2nd level caching 1st level caching only
This isn’t intended to be a NH vs. EF, and it doesn’t even pretend to be unbiased, I am simply pointing out a few examples of features that you can take advantage of which can greatly benefit you in one situation, which do not exists in another.
It has a high cost
In order to facilitate this data access encapsulation, you have to do one of two things:
- Use the lowest common denominator, preventing you from using the real benefits of the OR/M in question.
- Bleed those features through the DAL, allowing you to make use of those features, but preventing you from switching at a later time.
Either of those add complexity, reduce flexibility and creates confusion down the road. And in general, it still doesn’t work.
There are other barriers than the API
Here is an example from a real client, which insists on creating this encapsulation and hiding NHibernate inside their DAL. They run into the previously mentioned problems, where there are NHibernate features specifically designed to solve some of their problems, but which they have hard time to implement through their DAL.
Worse, from the migration perspective, most of the barrier for moving from NHibernate isn’t in the API. Their entity model make a heavy use on NHibernate’s <any/> feature, which large percentage other OR/Ms do not support. And that is merely the example that spring most forcibly to mind, there are others.
The real world doesn’t support it, even for the simplest scenarios
A while ago I tried porting the NerdDinner application to NHibernate. Just to point it out, that application have a single entity, and was designed with a nice encapsulation between the data access and the rest of the code. In order to make the port, I had to modify significant parts of the codebase. And that is about the simplest example that can be.
The role of encapsulation
Now, I know that some people would read this as an attack of encapsulation of data access, but that isn’t the case. By all mean, encapsulate to your heart’s content. But the purpose of this encapsulation is important. Trying to encapsulate to make things easier to work with, great. Trying to encapsulate so that you can switch OR/Ms? Won’t work, will be costly and painful.
So how do you move between OR/Ms?
There are reasons why some people want to move from one data access technology to the other. I was involved in several such efforts, and the approach that we used in each of those cases was porting, rather than trying to drop a new IDaataAccess implementation.