Geeks With Blogs
// ThomasWeller C#/.NET software development, software integrity, life as a freelancer, and all the rest

In previous parts (see here and here, I will refer to the examples therein throughout the following), I described a problem with O/R-mappers like NHibernate that could possibly break domain code which is relying on type information in some way or the other (this is not only relevant for casting issues!). The problem occurs when NH creates a lazy loading proxy for a polymorphic object, not knowing the exact type of the proxy at creation time. - Sure, you can easily circumvent the described issue if you know what's going on behind the scenes, but having to know about the persistence system and its internals means that you are not really persistence ignorant, even though your business layer may not have any physical dependency on the persistence layer.

In the beginning, I planned this to be a simple two-part post, with the first part demonstrating the problem and the second part showing a possible solution. But there's far too much to talk about to put it into a single blog post, and so I decided to make some more parts out of this. Currently, I intend to make two more posts in this series after this one - but that may not be the final say...

This post will present the first part of a possible way to circumvent the previously described issues and to be truly persistent ignorant - not just physically, but also logically. To achieve that goal, we first need to think about our everyday understanding of how NH works.

Some background: Watch out for the hidden stuff !

Normally, we think of NHibernate as a component that maps database rows to business objects via the mappings that we define. This view is certainly good enough for most scenarios, where we just want to save/load some object instances to/from a database. The concept that we usually have in mind looks roughly like this:

db-domain

But what's actually going on under the hood is visualized more accurately with this image:

db-entity-domain

NHibernate doesn't stuff the values from the database directly into the POCOs of our domain, instead there is an intermediate, memory-only layer that deals with so-called 'entities' and lets NH handle all the really complicated things behind the scene.

Now the interesting part for us here is that we can have attributes on an entity that do not correspond to any attributes on the associated domain object, thus being invisible on the domain surface. Such data are ordinary mappings (with all implications that this may bring), just that the none access-strategy is defined for them like so:

<property name="InvisibleAttribute" type="SomeType" access="none"/>

(Remark: The none keyword is new to NH v2.1, previous versions accept only the synonym noop.)

So, when loading a Book instance, we can have not only the usual state attributes, but also a memory-only attribute that holds the discriminator value for the polymorphic Author property, which is what we need here. For this to work, we must influence the way NH generates the SQL statements for our Book instances.

Defining custom SQL for an entity

Remember that the type discriminator for Author instances is not part of the BOOKS table, but is located in the referenced AUTHORS table. Thus, just declaring  a respective property without defining the SQL query that NH should use to fetch it from the db would lead to an error saying that there is no corresponding column in the BOOKS table. Fortunately, NH allows for defining custom SQL queries for just about everything. So we define a named query to fetch the desired discriminator value from the db, in addition to the 'regular' Book values, and reference it with the loader attribute. We have to handle this also for the opposite direction: when NH is trying to persist a BOOK instance, it will generate an SQL INSERT/UPDATE statement that tries to insert our additional value into the BOOKS table, again leading to the aforementioned error. But since we don't want the discriminator value to be persisted at all here, we can simple give the insert and update attributes a value of false to exclude it from the respective SQL generation.

The final mapping then looks like this:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="LLDemo.Core"

                   namespace="LLDemo.Core.Implementation"

                   default-access="nosetter.camelcase">

  <class name="Book" table="BOOKS">

    <id name="Id">

      <generator class="native" />

    </id>

    <property name="Name" not-null="true" />

    <many-to-one name="Author" class="AuthorBase" cascade="all" not-null="true"/>

    <property name="Author__discriminator__" type="System.Char"

              access="none" insert="false" update="false"/>

    <loader query-ref="loadbook"/>

  </class>

  <sql-query name="loadbook">

    <return alias="b" class="Book"/>

    SELECT

      BOOKS.ID AS {b.Id},

      BOOKS.NAME AS {b.Name},

      BOOKS.AUTHOR as {b.Author},

      AUTHORS.TYPE AS {b.Author__discriminator__}

    FROM BOOKS

    INNER JOIN AUTHORS ON BOOKS.AUTHOR = AUTHORS.ID

    WHERE BOOKS.ID=?

  </sql-query>

</hibernate-mapping>

With that in place, we now always have the required value at hand to make NH create a strongly-typed dynamic proxy for the Author property, instead of just a generic one, derived from the abstract AuthorBase type.

The next part will show you how this data can be accessed and evaluated at runtime, and how to make NH generate the desired strongly-typed dynamic proxy...

Posted on Sunday, September 20, 2009 7:28 AM NHibernate , Architecture and Design | Back to top


Comments on this post: Lazy loading, Inheritance, and Persistence ignorance (part 2)

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Thomas Weller | Powered by: GeeksWithBlogs.net