Browsing Posts published in February, 2011

[Code for this article is available on GitHub here.]

NHibernate 3 introduces support for both the .NET 3.5 Client Profile and .NET 4.0 Client Profile. This means that applications built with NHibernate can be deployed to client desktops without the full .NET Framework installed. Why hasn’t this been available all along?

NHibernate 2.X was built to support .NET 2.0 and above. The Client Profile wasn’t introduced until after NHibernate 2.0 was released. Microsoft introduced the Client Profile in .NET 3.5 to reduce the size of the .NET Framework on client machines. (The reality is that end users don’t need ASP.NET, server-side WCF features, and MSBuild on their machines to run client-side applications.)

So why didn’t NHibernate support the Client Profile once it was released? What was holding them back? What was holding NHibernate back from supporting the Client Profile immediately was a dependency on System.Web. Now why the heck would NHibernate depend on System.Web? There aren’t many places that NHibernate touches System.Web, but there are a few. The first is in supporting session-per-request semantics in web applications using Contextual Sessions. I won’t go into the details here, but once you configure cfg.CurrentSessionContext<T>() in Loquacious or hibernate.current_session_context_class in hibernate.cfg.xml, you can get the current session from your static session factory. (ASIDE: If none of the built-in ICurrentSessionContext classes suffices for your needs, it is very easy to implement your own.)

var session = sessionFactory.GetCurrentSession();

The ManagedWebSessionContext and WebSessionContext classes can be used for session-per-request semantics and both store the current session in the HttpContext. Hence they need a reference to System.Web. So to support the Client Profile, the NHibernate team had to break this dependency on System.Web. They did this by accessing the HttpContext via a compiled dynamic method, which is evaluated at run-time. (A compiled dynamic method has much better performance than accessing properties through reflection.)

Another more insidious dependency on System.Web was in the logging infrastructure. Before NHibernate 3, NHibernate took a hard dependency on log4net. If you wanted logging, you used log4net. Now here is the insidious part… log4net has a dependency on System.Web for its AspNetTraceAppender, which writes to the ASP.NET TraceContext. (You can access the ASP.NET TraceContext via http://example.com/trace.axd.) To break this dependency, NHibernate 3 introduces LoggerProvider and the IInternalLogger. If a logger is explicitly configured, it uses that one. Now for a bit of cleverness. If no logger is explicitly configured, and LoggerProvider is asked for an IInternalLogger, it checks the bin directory. If it finds log4net, it uses log4net. Otherwise it defaults to the NoLoggingLogger. (N.B. Out-of-the-box NHibernate 3 only supports log4net or no logging, though it isn’t too onerous support other logging frameworks by implementing an IInternalLogger adapter and some support classes.)

I haven’t done an exhaustive search of the NHibernate 2.X codebase looking for other dependencies on System.Web, but those two give you an idea of why supporting the .NET Client Profile wasn’t as simple as recompiling NHibernate 2.X. The team had to break some dependencies on assemblies not include with the .NET Client Profile while not breaking backward compatibility. For most developers, supporting the .NET Client Profile is as simple as switching the Target Framework on their assemblies.

ClientProfileTargetFramework

One word of warning… If you’re using NHibernate Profiler (and you should be), the NHibernateProfiler.Appender doesn’t support the .NET Client Profile because it requires log4net. You can use a preprocessor directive around the initialization line of NHibernate Profiler and then define that conditional compilation constant (using #define ENABLE_NHPROF) to enable/disable profiling. You’ll also have to change the target framework to .NET 3.5 or .NET 4.0 temporarily for the profiling session so that your project compiles.

#if ENABLE_NHPROF
HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();
#endif

UPDATE: NHibernate Profiler build 796 and later supports profiling applications built against the .NET Client Profile. More information can be found here.

[Code for this article is available on GitHub here.]

Nothing gets an OO zealot hot under the collar the way the term polymorphism does. You probably have three questions right now… What does polymorphism have to do with object-relational mapping? How does it relate to NHibernate? And why should I care?

An ORM that supports polymorphic loading allows us to request one type of object, but potentially get an object of a derived type back. As an example, let’s say we have the following simple inheritance hierarchy in our application:

Animal Inheritance Hierarchy

We can query for an Animal, but receive back an instance of Dog or Cat instead.

var dog = session.Get<Animal>(dogId);

NHibernate has supported this type of polymorphic loading behaviour for awhile, but the base class (or interface) had to be mapped. If it wasn’t, polymorphic loading would only work when querying with Criteria or LINQ. The following works for both NH 2.1.2 and NH3 regardless of whether the Animal base class is mapped or not.

var animal = session.CreateCriteria<Animal>()
                    .Add(Restrictions.IdEq(dogId))
                    .UniqueResult<Animal>();

// N.B. Use session.Linq<Animal>() in NH2.1.2
var query = from a in session.Query<Animal>()
            where a.Id == dogId
            select a;
var animal = query.Single();

In NHibernate 2.1.2 and earlier, ISession.Get<T>(id) or ISession.Load<T>(id) would fail if T was an unmapped base class or interface. With NHibernate 3, these methods now work regardless of whether T is mapped or not.*

// Works in NH3; works in NH2.1.2 only if Animal is mapped
// In the sample code, works in NH3 for both Animal and UnmappedAnimal base classes
// In NH2.1.2 and before, works for Animal (mapped), but not UnmappedAnimal
var dog = session.Get<Animal>(dogId);
var cat = session.Load<Animal>(catId);

ASIDE: ISession.Get(id) returns null when the entity doesn’t exist in the database, whereas ISession.Load(id) throws an exception. Generally ISession.Load(id) is preferred if you know the entity should exist as NHibernate can return a proxy object that delays hitting the database until the last possible moment. ISession.Get(id) requires querying the database immediately because there is no way to return an object (e.g. a proxy), but later change it to null when accessed.

In NHibernate 3, polymorphic loading works for Criteria, LINQ, and Get/Load. It has not been implemented for HQL. (If you want/need this feature, the NHibernate team is always willing to accept a feature request with patch.) HQL in NH3 supports polymorphic loading if the queried class is imported via <import class=”UnmappedClass”/> in a hbm.xml file.

// Criteria works in NH2.1.2 and NH3
var animal = session.CreateCriteria<UnmappedAnimal>()
                    .Add(Restrictions.IdEq(dogId))
                    .UniqueResult<UnmappedAnimal>());

// LINQ works in NH2.1.2 and NH3 (NH2.1.2 uses session.Linq<T>())
var query = from a in session.Query<UnmappedAnimal>()
            where a.Id == dogId
            select a;
var animal = query.Single();

// Get/Load works in NH3, but fails in NH2.1.2 and earlier
var animal = session.Get<UnmappedAnimal>(dogId);

// HQL works for NH3 if UnmappedAnimal is imported, but fails for NH2.1.2
var animal = session.CreateQuery("from a in UnmappedAnimal where a.id = :id")
                    .SetParameter("id", dogId)
                    .UniqueResult<UnmappedAnimal>());

* I should note one restriction on the generic parameter T when calling ISession.Get<T>(id) and ISession.Load<T>(). Polymorphic loading only works if there is a unique persister for T. Otherwise NHibernate throws a HibernateException, “Ambiguous persister for [T] implemented by more than one hierarchy”. What does this mean? Let’s say you have an unmapped abstract base class, such as Entity. (Entity is a class defined in our application, which includes properties common across all persistent entities, such as primary key, audit fields, and similar. It is not required by NHibernate, but often useful for extracting common domain code.) Consider the following contrived example:

Contrived Inheritance Hierarchy

Note that the Animal inheritance hierarchy is mapped and so is Customer. If we try to execute the following code:

var id = 42;
var entity = session.Get<Entity>(id);

We will get a HibernateException as mentioned above. We are asking NHibernate to load an Entity with an id of 42. But primary keys are only unique within a mapped inheritance hierarchy. So there could be a Cat (or Dog) with id of 42 and a Customer with id of 42! So NHibernate fails with a HibernateException since it has no way of returning a list of objects from Get/Load. If you really want to query across inheritance hierarchies, you can do so with Critera or LINQ where you return a list of objects. The following code will work:

var id = 42;
var entities = session.CreateCriteria<Entity>()
                      .Add(Restrictions.IdEq(id))
                      .List<Entity>();

Here’s a NHibernate trick that makes for a good demo, but isn’t terribly practical in real applications… Retrieve a list of all entities in the database:

var allEntities = session.CreateCriteria<object>()
                         .List<object>();

Happy coding!

UPDATE: Fabio Maulo, NH project lead, pointed out to me that HQL in NHibernate 3 can load unmapped classes so long as you make NHibernate aware of the classes via an <import class=”UnmappedAnimal”/> directive in a hbm.xml file. Thanks, Fabio.