I've been taking LINQ to NHibernate (Alpha just released) for a test drive in my latest project and so far I'm pleased enough that I think I will stick with it going forward. This post is to point out a subtle behaviour that caught me unawares. Consider the following example:
session.Linq<Foo>()
.WithId(123)
.Single();
The first line is LINQ to NHibernate speak which simply returns an IQueryable<Foo> representing all available objects of type Foo. The important thing here is that it is just a query, the database has not been visited yet. The second line is my own extension method which should extend the query and filter the results by Id:
public static IEnumerable<Foo> WithId(
this IEnumerable<Foo> foos, int id)
{
return foos.Where(x => x.Id == id);
}
The third line is a standard LINQ to SQL function which makes sure that there is only one item in the list and returns it for you. This call is the first time that the code should touch the database and the result should be that only a single row is returned. Unfortunately one of the tests I wrote today turned up something suspicious... my extension method was being called 23 times and by coincidence there also happened to be 23 items in my database!
Since the code actually produced the correct results and this was really a performance issue I decided it would be a good time to check out Ayende's new NHibernate Profiler. Overall I am quite impressed as it is easy to set up, shows me everything I expect and has a reasonably intuitive interface for a developer tool. Compared to setting up a trace on the SQL server and trying to analyse the results, using the profiler was actually fun.
Now certain that my code was eagerly requesting every row in the table then throwing them away, I set out to figure out why. Using the traditional poke it with the debugger approach, I discovered that changing my extension method to this resolved the problem:
public static IQueryable<Foo> WithId(
this IQueryable<Foo> foos, int id)
{
return foos.Where(x => x.Id == id);
}
Why did I think IEnumerable<T> was a good choice? Because IQueryable<T> inherits directly from it and I was trying to use the less restrictive interface. Why does it not work like I expect? Because there is a Where extension method for each of the interfaces and a quick look with Reflector shows that they do very, very different things.
Extension methods can't be overridden like virtual methods which means objects will act like the name you give them, not like what they really are. This also means that it's not LINQ to NHibernate's fault at all, in fact you will likely run into this issue with LINQ to SQL as well.
If there's a lesson to be learnt from this story it's probably to be more careful of extension methods. I can't really call my own misunderstandings bugs in the framework so instead I'm just going to make some snarky remarks about un-intuitive behaviour and sign off...
21/01/2009 03:35 AM (UTC -08:00)
I've become quite a fan of Eric Evans book, Domain Driven Design. Well, the first half at least. Part of my recent work has been been moving towards the persistence ignorance people in this space push for so much. All this really means is creating a domain layer that is not aware of, and has no dependency on the way it is stored.
Microsoft recently released the ADO.Net Entity Framework which was lots of fun for the ALT.NET crowd because it gave them something new to complain about. One of the things that kept coming up was that Microsoft's new framework wasn't going to support the kind of persistence ignorance many of us are looking for, though apparently it will move towards this in version two. One of the most cited alternatives is NHibernate, the .Net port of Hibernate, the popular Java ORM (object relational mapping) tool.
Though I've used a few ORM tools in the past I haven't used NHibernate, so I thought I would give it a try so I could get the most enjoyment out of the enthusiastic debate over Microsoft's latest toy. When I began using the library I already had some domain objects in place and was pretty happy with how they were sitting. The following are observations made while converting my repositories (data access layer for those not hip with DDD) over to use NHibernate. Note that I am using the library directly, rather than through another layer such as Active Record.
No Dependencies
True to it's word NHibernate lets you create your domain classes without doing any of the following:
- Inheriting from a particular class.
- Implementing a particular interface.
- Attaching any attributes.
- Referencing the NHibernate DLL in any way.
This is really rather refreshing after working with some alternate products that do place these restrictions on you and make it very difficult to move to a different storage mechanism in future. In my case this is particularly useful as I have some data stored in SQL Server, some data accessed via web services and some data on disk but would like to hide that from my application and present it all through the same set of domain classes.
Some Restrictions
While your domain will be free of direct dependencies on the NHibernate library, it does enforce a couple of interesting design decisions on you.
The first thing I ran into was that all of the objects being mapped are required to have a visible default constructor, i.e. one that takes no parameters and is not private. There are sound technical reasons behind this restriction but it did run against the way I had set up my domain. In the end I settled for having my default constructor protected for the mapping and my normal constructor public for the users of the domain. I would like to see an option to specify a mapping to a non default constructor though I'm not sure how feasible this would be to add to the library.
The second thing required of my domain classes was that all of the properties I was mapping had to be virtual. This seems a little odd at first suggestion but it is how you enable NHibernate to perform some of it's more advanced functions such as using proxies and lazy loading. The extra keyword on each property makes negligible difference and is possibly a good idea in some cases anyway. Apparently it is possible to disable this restriction if you are willing to sacrifice some functionality.
So yes, you can write persistence ignorant domain classes, but be prepared to still make some small concessions. In some ways I question whether these requirements are a dependency in disguise but because they are based on the fundamentals of .Net, they are general enough not to bother me. It seems like the only way we are going to get total freedom from the database is by letting go of our tried and trusted statically typed languages.
Flexible Mappings
Most of my ORM experience has been with older or simpler tools where the structure of your objects is really determined by how you lay out your SQL tables. Though it's certainly not unique in this regard, NHibernate gives you plenty of mapping options which let you have distinctly different models for your domain and database objects.
One pleasing feature is the ability to map a custom concept in your domain, such as a class called Kilograms, to one or more SQL columns within a table, such as an integer. This allows you to provide a very strong domain model while still maintaining that automatic mapping back to the basic SQL types. Doing this does require creating a small mapping object derived from IUserType but the pay off is worth it in my case. I need to explore a little more in this area because I suspect much of the IUserType implementation is generic is the simple cases.
Though I haven't used it yet there is also an ability to map part of a SQL table to a separate class to help simplify large objects. The usual example is mapping columns such as Street, Suburb and City to and Address object.
Session Integrity
One of the reasons I hadn't looked into the library was based on faulty assumptions that it would have the same flaws other ORM products I used had. In past projects a common source of weird an unusual behaviour has been the data in the database not keeping up to date with changes that had been made to the data in memory. A simple example is a query to select all items not including new items that have been created but not committed yet.
NHibernate refers to those in memory copies of the data as the current session and solves the problem by automatically flushing any changes to the database whenever it detects these sorts of issues may arise. This means as long as you are using transactions, which you should be, you shouldn't have any issues. You also get a couple of niceties along with it such as generated primary keys being updated as soon as you make NHibernate aware of new objects.
NHibernate, Good Times
Overall a big thumbs up from me and I will continue to use
NHibernate in future. I'm especially interested to see the upcoming version which is expected to include LINQ querying support.
08/10/2008 12:16 AM (UTC -07:00)