Projecting unmapped Ids with Linq-to-NHibernate
November 5th, 2009
There are situations when you do not need to get the fully tracked entities using NHibernate — you know you wouldn’t ever edit them and need minimum overhead for this specific scenario. One example is AJAX auto-completion. On the other hand, if you are as obsessed with architectural purity as me, you probably do not have Id properties in your entities, since they are artefacts of (relational) DBs.
So there is the question: in UI layer we want to do
repository.Query().Select(x => new ListItem { Key = x.Id, Name = x.Name })
But there are no “Id” properties in our entities. How can we do this (without returning to the dark ages of untyped criteria)?
There is a very simple (and working) answer. Let’s start with how I do it for individual entities. When I need a key/id in the UI to identify the entity between requests, I use repository.GetKey(entity), which internally calls session.GetIdentifier(entity). Simple and not intrusive into domain logic. Now,
repository.Query().Select(x => new ListItem { Key = GetKey(x), Name = x.Name })
is obviously impossible, since HQL/DB can not understand GetKey call.
Ok, so the solution is to pre-process the call before Linq-to-NHibernate and replace GetKey call with reference to fake property named “id”, which is a magic name NHibernate understands as identifier reference. Linq-to-NHibernate even provides public expression visitor, so it was trivial to create KeyMethodToIdRewritingVisitor (the fake PropertyInfo took most effort, which had to have some stuff to fool Expression.Property).
You can get resulting code below.
It is not perfect, but it works and flaws are really easy to polish out.